gpt4 book ai didi

java - 在单次基准测试中刷新缓存行

转载 作者:行者123 更新时间:2023-12-05 05:05:47 26 4
gpt4 key购买 nike

我想运行一个 SingleShot JMH 基准测试,所有与正在运行的内存相关的缓存层次结构都被可靠地刷新。

基准大致如下:

@State(Scope.Benchmark)
public class MyBnchmrk {
public byte buffer[];

@Setup(Level.Trial)
public void generateSampleData() throws IOException {
// writes to buffer ...
}

@Setup(Level.Invocation)
public void flushCaches() {
//Perfectly I'd like to invoke here something like
//_mm_clflushopt() intrinsic as in GCC/clang for each line of the buffer
}

@Benchmark
@BenchmarkMode(Mode.SingleShotTime)
public void benchmarkMemoryBoundCode() {
//the benchmark
}
}

在需要单次测量或手写 clflush 之前是否有 Java 方法来刷新缓存?

最佳答案

如果你想测量缓存未命中访问,直接从 java 调用 clflush 是可能的,但你最终会编写带有 ASM 内在函数的 JNI 库。更不用说,您可能无法以可靠的方式执行此操作,因为您需要提供虚拟地址,并且 GC 可能会随时移动您的缓冲区。

相反,我给你这个:

  • 像您一样使用单一快照基准测试
  • 测量单次操作不是一个好主意(测量纳秒的误差很大)。相反,创建数百万个相同的缓冲区并对数百万个缓冲区执行相同的操作。每次访问不在缓存中的下一个缓冲区时
  • 您还可以在迭代之间运行一些计算。例如,读取 32+ mb 的内存,以便它从缓存中逐出缓存行。但是有百万个缓冲区,它没有显示任何利润

结果代码:

    @State(Scope.Benchmark)
@BenchmarkMode(Mode.SingleShotTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Fork(value = 1)
public class BufferBenchmarkLatency {

public static final int BATCH_SIZE = 1000000;

public static final int MY_BUFFER_SIZE = 1024;
public static final int CACHE_LINE_PADDING = 256;

public static class StateHolder extends Padder {
byte buffer[];

StateHolder() {
buffer = new byte[CACHE_LINE_PADDING + MY_BUFFER_SIZE + CACHE_LINE_PADDING];
Arrays.fill(buffer, (byte) ThreadLocalRandom.current().nextInt());
}
}

private final StateHolder[] arr = new StateHolder[BATCH_SIZE];
private int index;

@Setup(Level.Trial)
public void setUpTrial() {
for (int i = 0; i < arr.length; i++) {
arr[i] = new StateHolder();
}
ArrayUtil.shuffle(arr)
}

@Setup(Level.Iteration)
public void prepareForIteration(Blackhole blackhole) {
index = 0;
blackhole.consume(CacheUtil.evictCacheLines());
System.gc();
System.gc();
}

@Benchmark
public long read() {
byte[] buffer = arr[index].buffer;
return buffer[0];
}

@TearDown(Level.Invocation)
public void move() {
index++;
}

public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(BufferBenchmarkLatency.class.getSimpleName())
.measurementBatchSize(BATCH_SIZE)
.warmupBatchSize(BATCH_SIZE)
.measurementIterations(10)
.warmupIterations(10)
.build();
new Runner(opt).run();
}
}

如您所见,我填充状态持有者本身,因此读取缓冲区引用总是在不同的缓存行上(Padder 类有 24 个长字段)。哦,我也填充缓冲区本身,JMH 不会为你做这件事。

我已经实现了这个想法,对于读取缓冲区的第一个元素这样的简单操作,我得到了平均 100 ns 的结果。要读取第一个元素,您需要读取两个缓存行(缓冲区引用 + 第一个元素)。完整代码是 here

关于java - 在单次基准测试中刷新缓存行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60318387/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com