Create simple JMH benchmark to compare CCR and PCR#1908
Conversation
A simple JMH benchmark that compares classic and path conflict resolvers.
|
Nice benchmark! A few observations and a suggestion for a more representative topology. Minor issues in the current code
The "diamond" topology would better stress conflict resolutionThe current A diamond (or wide diamond fan) is the classic Maven conflict topology — the same artifact pulled in at different versions via two or more paths: Here's a /**
* A "diamond fan": {@code width} independent chains of length {@code depth}, all converging
* on the same shared artifact at two different versions (simulating a real version conflict).
* This is the topology where CCR's O(N²) re-walk per conflict group shows up most clearly.
*
* Graph shape (width=3, depth=2):
* root
* ├── chain-0-0 → chain-0-1 → shared:1.0
* ├── chain-1-0 → chain-1-1 → shared:2.0 (conflict: 1.0 vs 2.0)
* └── chain-2-0 → chain-2-1 → shared:1.0
*/
private static void diamondFan(ConflictResolver conflictResolver, int depth, int width)
throws RepositoryException {
DependencyNode root = makeDependencyNode("group-id", "root", "1.0");
List<DependencyNode> chains = new ArrayList<>(width);
for (int w = 0; w < width; w++) {
DependencyNode chainHead = makeDependencyNode("group-id", "chain-" + w + "-0", "1.0");
DependencyNode last = chainHead;
for (int d = 1; d < depth; d++) {
DependencyNode dep = makeDependencyNode("group-id", "chain-" + w + "-" + d, "1.0");
last.setChildren(mutableList(dep));
last = dep;
}
// All chains converge on the same artifact at alternating versions — real conflict
String version = (w % 2 == 0) ? "1.0" : "2.0";
DependencyNode shared = makeDependencyNode("group-id", "shared", version);
last.setChildren(mutableList(shared));
chains.add(chainHead);
}
root.setChildren(chains);
DependencyNode result = transform(conflictResolver, root);
assertNotNull(result);
// After conflict resolution, all paths to "shared" must agree on one version
assertEquals(1, countDistinctVersions(result, "shared"));
}
private static int countDistinctVersions(DependencyNode node, String artifactId) {
// BFS/DFS to count distinct versions of the given artifactId in the resolved graph
java.util.Set<String> versions = new java.util.HashSet<>();
collectVersions(node, artifactId, versions);
return versions.size();
}
private static void collectVersions(DependencyNode node, String artifactId, java.util.Set<String> versions) {
if (node.getArtifact() != null && artifactId.equals(node.getArtifact().getArtifactId())
&& node.getData().get(ConflictResolver.NODE_DATA_WINNER) == null) {
versions.add(node.getArtifact().getVersion());
}
for (DependencyNode child : node.getChildren()) {
collectVersions(child, artifactId, versions);
}
}Then add benchmark methods: @Benchmark
public void diamondFan_10x5_path() throws RepositoryException {
diamondFan(path, 5, 10);
}
@Benchmark
public void diamondFan_10x5_classic() throws RepositoryException {
diamondFan(classic, 5, 10);
}
@Benchmark
public void diamondFan_20x10_path() throws RepositoryException {
diamondFan(path, 10, 20);
}
@Benchmark
public void diamondFan_20x10_classic() throws RepositoryException {
diamondFan(classic, 10, 20);
}My expectation is that Would be interesting to see your numbers on this one! |
A simple JMH benchmark that compares classic and path conflict resolvers.
Note: the(is merged)uniqueSnakeWithRootCycleNPEs on master, fixed in #1904My local results:
Repeated run, as 125 was sus: