Для одного из моих университетских курсов мне необходимо реализовать реализацию стека без блокировок и выполнить его тестирование, чтобы увидеть, как оно масштабируется с учетом количества потоков и распределения операций.Недавно я обнаружил JMH и решил применить его к компоненту сравнительного анализа домашнего задания.Я просто не уверен, что я реализую это так элегантно, как мог бы.
Мой профессор хочет, чтобы мы измерили время, необходимое для выполнения 1, 2, 4, 8, 16 и 32 потоков3 разных распределения операций.Я смоделировал это ниже:
package kylemart.multicore.assignment2;
import org.openjdk.jmh.annotations.*;
// Annotations here...
public class P1StackBenchmarks {
@Benchmark
public void runScenarioA(ScenarioA scenario) throws InterruptedException {
runThreads(scenario.threads);
}
@Benchmark
public void runScenarioB(ScenarioB scenario) throws InterruptedException {
runThreads(scenario.threads);
}
@Benchmark
public void runScenarioC(ScenarioC scenario) throws InterruptedException {
runThreads(scenario.threads);
}
private void runThreads(Thread[] threads) throws InterruptedException {
for (Thread thread : threads) {
thread.start();
}
for (Thread thread : threads) {
thread.join();
}
}
@State(Scope.Benchmark)
public static class BenchmarkParameters {
@Param({"1", "2", "4", "8", "16", "32"})
int threadCount;
static final int iterations = 500_000;
}
@State(Scope.Thread)
public static abstract class Scenario {
Thread[] threads;
PStack<Object> stack;
Object object;
@Setup(Level.Invocation)
public void setup(BenchmarkParameters params) {
threads = new Thread[params.threadCount];
stack = new P1Stack<>();
object = new Object();
Runnable runnable = () -> {
for (int iteration = 0; iteration < params.iterations; iteration++) {
operations();
}
};
for (int index = 0; index < threads.length; index++) {
threads[index] = new Thread(runnable);
}
}
protected abstract void operations();
}
public static class ScenarioA extends Scenario {
@Override
protected void operations() {
// stack.push(object);
// ...
}
}
public static class ScenarioB extends Scenario {
@Override
protected void operations() {
// stack.push(object);
// ...
}
}
public static class ScenarioC extends Scenario {
@Override
protected void operations() {
// stack.push(object);
// ...
}
}
}
Каждый сценарий, A, B и C, имеет метод operations()
, который определяет набор операций стека с определенной частотой pop()
, * 1008.* и size()
звонки.Абстрактный класс Scenario является «шаблоном» для этих конкретных сценариев.Предполагая, что аннотации являются наследуемыми (что, по-видимому, имеет место - хотя не против того, чтобы кто-то проверял это), я бы ожидал, что экземпляры трех конкретных классов сценариев будут видоизменены в единичные потоки (то есть @State(Scope.Thread)
) иего метод setup()
запускается перед каждым вызовом эталонного метода (то есть @Setup(Level.Invocation)
).Благодаря этому я смогу создать и настроить потоки каждого сценария до того, как мы начнем запускать потоки.
Есть какие-либо проблемы с этой реализацией?Есть ли в JMH другие средства, которые облегчили бы решение / реализацию этой проблемы?Спасибо!