rajat315315 commented on PR #6721:
URL: https://github.com/apache/jmeter/pull/6721#issuecomment-5002727857
I used below benchmark script to compare the time taken.
It was auto-generated for me using an AI model.
```java
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to you under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.jmeter.threads;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.apache.jmeter.control.Controller;
import org.apache.jmeter.control.GenericController;
import org.apache.jmeter.control.LoopController;
import org.apache.jmeter.samplers.AbstractSampler;
import org.apache.jmeter.samplers.Entry;
import org.apache.jmeter.samplers.SampleResult;
import org.apache.jorphan.collections.ListedHashTree;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;
import org.openjdk.jmh.infra.Blackhole;
import org.openjdk.jmh.profile.GCProfiler;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
/**
* Benchmark comparing two approaches for retrieving a sampler's parent
* controller path in JMeterThread:
* <ul>
* <li><b>traversal</b> — original approach: traverse the entire HashTree
* with {@link FindTestElementsUpToRootTraverser} on every call
* (master branch behavior).</li>
* <li><b>compilerLookup</b> — optimized approach: O(1) lookup via
* {@link TestCompiler#getControllersForSampler} which reuses the
* controller path already computed during compilation.</li>
* </ul>
*/
@Fork(value = 2, jvmArgsPrepend = {"-Xmx256m"})
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@State(Scope.Thread)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public class ControllerPathLookupBenchmark {
/** Depth of nested controllers above the target sampler. */
@Param({"2", "5", "10"})
int treeDepth;
/** Number of sibling samplers at each controller level (widens the
tree). */
@Param({"1", "10"})
int siblingsPerLevel;
private ListedHashTree testTree;
private TestCompiler compiler;
private AbstractSampler targetSampler;
/** Minimal sampler implementation for benchmarking. */
private static class BenchmarkSampler extends AbstractSampler {
private static final long serialVersionUID = 1L;
BenchmarkSampler(String name) {
setName(name);
}
@Override
public SampleResult sample(Entry e) {
return null;
}
}
@Setup
public void setup() {
testTree = new ListedHashTree();
// Build a tree: root controller -> nested controllers -> samplers
// depth=2: Root -> Controller1 -> [targetSampler, sibling1, ...]
// depth=5: Root -> C1 -> C2 -> C3 -> C4 -> [targetSampler, ...]
// depth=10: Root -> C1 -> ... -> C9 -> [targetSampler, ...]
LoopController root = new LoopController();
root.setName("RootLoop");
root.setLoops(1);
ListedHashTree currentSubTree = (ListedHashTree) testTree.add(root);
// Create nested controllers
for (int d = 1; d < treeDepth; d++) {
GenericController ctrl = new GenericController();
ctrl.setName("Controller-" + d);
// Add sibling samplers at this level (to make the tree wider)
for (int s = 0; s < siblingsPerLevel; s++) {
BenchmarkSampler sibling = new BenchmarkSampler("Sibling-" +
d + "-" + s);
currentSubTree.add(ctrl, sibling);
}
currentSubTree = (ListedHashTree) currentSubTree.add(ctrl);
}
// Add the target sampler at the deepest level
targetSampler = new BenchmarkSampler("TargetSampler");
currentSubTree.add(targetSampler);
// Add more siblings at the deepest level
for (int s = 0; s < siblingsPerLevel; s++) {
currentSubTree.add(new BenchmarkSampler("DeepSibling-" + s));
}
// Compile the tree (populates samplerConfigMap)
TestCompiler.initialize();
compiler = new TestCompiler(testTree);
testTree.traverse(compiler);
}
/**
* OLD approach (master): traverse the full tree every time.
*/
@Benchmark
public List<Controller> traversal(Blackhole bh) {
FindTestElementsUpToRootTraverser traverser =
new FindTestElementsUpToRootTraverser(targetSampler);
testTree.traverse(traverser);
List<Controller> controllers = traverser.getControllersToRoot();
bh.consume(controllers);
return controllers;
}
/**
* NEW approach: O(1) lookup via TestCompiler's precomputed map.
*/
@Benchmark
public List<Controller> compilerLookup(Blackhole bh) {
List<Controller> controllers =
compiler.getControllersForSampler(targetSampler);
bh.consume(controllers);
return controllers;
}
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(ControllerPathLookupBenchmark.class.getSimpleName())
.addProfiler(GCProfiler.class)
.detectJvmArgs()
.build();
new Runner(opt).run();
}
}
```
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]