Browse Source
This test isn't that useful. The better way to evaluate diff algorithm performance is to run `jgit debug-diff-algorithms` over real-world repositories, such as linux-2.6.git. Whenever we modify an algorithm we should manually verify that its runtime performance doesn't get any worse than it already is. Change-Id: I0beed3a5a8a537c958a5a6438a1283f97fa2097a Signed-off-by: Shawn O. Pearce <spearce@spearce.org>stable-0.10
Shawn O. Pearce
14 years ago
4 changed files with 6 additions and 352 deletions
@ -1,213 +0,0 @@
|
||||
/* |
||||
* Copyright (C) 2009, Christian Halstrick <christian.halstrick@sap.com> |
||||
* and other copyright owners as documented in the project's IP log. |
||||
* |
||||
* This program and the accompanying materials are made available |
||||
* under the terms of the Eclipse Distribution License v1.0 which |
||||
* accompanies this distribution, is reproduced below, and is |
||||
* available at http://www.eclipse.org/org/documents/edl-v10.php
|
||||
* |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or |
||||
* without modification, are permitted provided that the following |
||||
* conditions are met: |
||||
* |
||||
* - Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* - Redistributions in binary form must reproduce the above |
||||
* copyright notice, this list of conditions and the following |
||||
* disclaimer in the documentation and/or other materials provided |
||||
* with the distribution. |
||||
* |
||||
* - Neither the name of the Eclipse Foundation, Inc. nor the |
||||
* names of its contributors may be used to endorse or promote |
||||
* products derived from this software without specific prior |
||||
* written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND |
||||
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, |
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
|
||||
package org.eclipse.jgit.diff; |
||||
|
||||
import java.text.DecimalFormat; |
||||
import java.text.NumberFormat; |
||||
import java.util.Collections; |
||||
import java.util.Comparator; |
||||
import java.util.LinkedList; |
||||
import java.util.List; |
||||
|
||||
import junit.framework.TestCase; |
||||
|
||||
import org.eclipse.jgit.util.CPUTimeStopWatch; |
||||
|
||||
/** |
||||
* Test cases for the performance of the diff implementation. The tests test |
||||
* that the performance of the MyersDiff algorithm is really O(N*D). Means the |
||||
* time for computing the diff between a and b should depend on the product of |
||||
* a.length+b.length and the number of found differences. The tests compute |
||||
* diffs between chunks of different length, measure the needed time and check |
||||
* that time/(N*D) does not differ more than a certain factor. |
||||
*/ |
||||
public class DiffPerformanceTest extends TestCase { |
||||
private static final long longTaskBoundary = 5000000000L; |
||||
|
||||
private static final int minCPUTimerTicks = 10; |
||||
|
||||
private static final int maxFactor = 15; |
||||
|
||||
private CPUTimeStopWatch stopwatch=CPUTimeStopWatch.createInstance(); |
||||
|
||||
public class PerfData { |
||||
private NumberFormat fmt = new DecimalFormat("#.##E0"); |
||||
|
||||
public long runningTime; |
||||
|
||||
public long D; |
||||
|
||||
public long N; |
||||
|
||||
private double p1 = -1; |
||||
|
||||
private double p2 = -1; |
||||
|
||||
public double perf1() { |
||||
if (p1 < 0) |
||||
p1 = runningTime / ((double) N * D); |
||||
return p1; |
||||
} |
||||
|
||||
public double perf2() { |
||||
if (p2 < 0) |
||||
p2 = runningTime / ((double) N * D * D); |
||||
return p2; |
||||
} |
||||
|
||||
public String toString() { |
||||
return ("diffing " + N / 2 + " bytes took " + runningTime |
||||
+ " ns. N=" + N + ", D=" + D + ", time/(N*D):" |
||||
+ fmt.format(perf1()) + ", time/(N*D^2):" + fmt |
||||
.format(perf2()) + "\n"); |
||||
} |
||||
} |
||||
|
||||
public static Comparator<PerfData> getComparator(final int whichPerf) { |
||||
return new Comparator<PerfData>() { |
||||
public int compare(PerfData o1, PerfData o2) { |
||||
double p1 = (whichPerf == 1) ? o1.perf1() : o1.perf2(); |
||||
double p2 = (whichPerf == 1) ? o2.perf1() : o2.perf2(); |
||||
return (p1 < p2) ? -1 : (p1 > p2) ? 1 : 0; |
||||
} |
||||
}; |
||||
} |
||||
|
||||
public void test() { |
||||
if (stopwatch!=null) { |
||||
// run some tests without recording to let JIT do its optimization
|
||||
test(10000); |
||||
test(20000); |
||||
test(10000); |
||||
test(20000); |
||||
|
||||
List<PerfData> perfData = new LinkedList<PerfData>(); |
||||
perfData.add(test(10000)); |
||||
perfData.add(test(20000)); |
||||
perfData.add(test(40000)); |
||||
perfData.add(test(80000)); |
||||
perfData.add(test(160000)); |
||||
perfData.add(test(320000)); |
||||
perfData.add(test(640000)); |
||||
perfData.add(test(1280000)); |
||||
|
||||
Comparator<PerfData> c = getComparator(1); |
||||
double factor = Collections.max(perfData, c).perf1() |
||||
/ Collections.min(perfData, c).perf1(); |
||||
assertTrue( |
||||
"minimun and maximum of performance-index t/(N*D) differed too much. Measured factor of " |
||||
+ factor |
||||
+ " (maxFactor=" |
||||
+ maxFactor |
||||
+ "). Perfdata=<" + perfData.toString() + ">", |
||||
factor < maxFactor); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Tests the performance of MyersDiff for texts which are similar (not |
||||
* random data). The CPU time is measured and returned. Because of bad |
||||
* accuracy of CPU time information the diffs are repeated. During each |
||||
* repetition the interim CPU time is checked. The diff operation is |
||||
* repeated until we have seen the CPU time clock changed its value at least |
||||
* {@link #minCPUTimerTicks} times. |
||||
* |
||||
* @param characters |
||||
* the size of the diffed character sequences. |
||||
* @return performance data |
||||
*/ |
||||
private PerfData test(int characters) { |
||||
PerfData ret = new PerfData(); |
||||
String a = DiffTestDataGenerator.generateSequence(characters, 971, 3); |
||||
String b = DiffTestDataGenerator.generateSequence(characters, 1621, 5); |
||||
CharArray ac = new CharArray(a); |
||||
CharArray bc = new CharArray(b); |
||||
CharCmp cmp = new CharCmp(); |
||||
int D = 0; |
||||
int cpuTimeChanges = 0; |
||||
long lastReadout = 0; |
||||
long interimTime = 0; |
||||
int repetitions = 0; |
||||
stopwatch.start(); |
||||
while (cpuTimeChanges < minCPUTimerTicks && interimTime < longTaskBoundary) { |
||||
D = MyersDiff.INSTANCE.diff(cmp, ac, bc).size(); |
||||
repetitions++; |
||||
interimTime = stopwatch.readout(); |
||||
if (interimTime != lastReadout) { |
||||
cpuTimeChanges++; |
||||
lastReadout = interimTime; |
||||
} |
||||
} |
||||
ret.runningTime = stopwatch.stop() / repetitions; |
||||
ret.N = ac.size() + bc.size(); |
||||
ret.D = D; |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
static class CharArray extends Sequence { |
||||
final char[] array; |
||||
|
||||
public CharArray(String s) { |
||||
array = s.toCharArray(); |
||||
} |
||||
|
||||
@Override |
||||
public int size() { |
||||
return array.length; |
||||
} |
||||
} |
||||
|
||||
static class CharCmp extends SequenceComparator<CharArray> { |
||||
@Override |
||||
public boolean equals(CharArray a, int ai, CharArray b, int bi) { |
||||
return a.array[ai] == b.array[bi]; |
||||
} |
||||
|
||||
@Override |
||||
public int hash(CharArray seq, int ptr) { |
||||
return seq.array[ptr]; |
||||
} |
||||
} |
||||
} |
@ -1,93 +0,0 @@
|
||||
/* |
||||
* Copyright (C) 2009, Christian Halstrick <christian.halstrick@sap.com> |
||||
* and other copyright owners as documented in the project's IP log. |
||||
* |
||||
* This program and the accompanying materials are made available |
||||
* under the terms of the Eclipse Distribution License v1.0 which |
||||
* accompanies this distribution, is reproduced below, and is |
||||
* available at http://www.eclipse.org/org/documents/edl-v10.php
|
||||
* |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or |
||||
* without modification, are permitted provided that the following |
||||
* conditions are met: |
||||
* |
||||
* - Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* - Redistributions in binary form must reproduce the above |
||||
* copyright notice, this list of conditions and the following |
||||
* disclaimer in the documentation and/or other materials provided |
||||
* with the distribution. |
||||
* |
||||
* - Neither the name of the Eclipse Foundation, Inc. nor the |
||||
* names of its contributors may be used to endorse or promote |
||||
* products derived from this software without specific prior |
||||
* written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND |
||||
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, |
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
|
||||
package org.eclipse.jgit.diff; |
||||
|
||||
public class DiffTestDataGenerator { |
||||
/** |
||||
* Generate sequence of characters in ascending order. The first character |
||||
* is a space. All subsequent characters have an ASCII code one greater then |
||||
* the ASCII code of the preceding character. On exception: the character |
||||
* following which follows '~' is again a ' '. |
||||
* |
||||
* @param len |
||||
* length of the String to be returned |
||||
* @return the sequence of characters as String |
||||
*/ |
||||
public static String generateSequence(int len) { |
||||
return generateSequence(len, 0, 0); |
||||
} |
||||
|
||||
/** |
||||
* Generate sequence of characters similar to the one returned by |
||||
* {@link #generateSequence(int)}. But this time in each chunk of |
||||
* <skipPeriod> characters the last <skipLength> characters are left out. By |
||||
* calling this method twice with two different prime skipPeriod values and |
||||
* short skipLength values you create test data which is similar to what |
||||
* programmers do to their source code - huge files with only few |
||||
* insertions/deletions/changes. |
||||
* |
||||
* @param len |
||||
* length of the String to be returned |
||||
* @param skipPeriod |
||||
* @param skipLength |
||||
* @return the sequence of characters as String |
||||
*/ |
||||
public static String generateSequence(int len, int skipPeriod, |
||||
int skipLength) { |
||||
StringBuilder text = new StringBuilder(len); |
||||
int skipStart = skipPeriod - skipLength; |
||||
int skippedChars = 0; |
||||
int block = 0; |
||||
for (int i = 0; i - skippedChars < len; ++i) { |
||||
if ((i % skipPeriod) == 1) |
||||
text.append((char) (256 + block++)); |
||||
else if (skipPeriod == 0 || i % skipPeriod < skipStart) { |
||||
text.append((char) (32 + i % 95)); |
||||
} else { |
||||
skippedChars++; |
||||
} |
||||
} |
||||
return text.toString(); |
||||
} |
||||
} |
Loading…
Reference in new issue