2022-07-29

CPU costs of Thread.sleep() and Thread.onSpinWait()

I'm running two benchmarks in order to compare costs of Thread.sleep() and Thread.onSpinWait():

@State(Scope.Thread)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
public class ThreadSleep2Benchmark {
  private final ExecutorService executor = Executors.newFixedThreadPool(1);
  volatile boolean run;

  @Param({"1", "5", "10", "50", "100"})
  long delay;

  @Setup(Level.Invocation)
  public void setUp() {
    run = true;
    startThread();
  }

  @TearDown(Level.Trial)
  public void tearDown() {
    executor.shutdown();
  }

  @Benchmark
  public int sleep() throws Exception {
    while (run) {
      Thread.sleep(1);
    }
    return hashCode();
  }

  private void startThread() {
    executor.submit(() -> {
      try {
        Thread.sleep(delay / 2);
        run = false;
      } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
        throw new RuntimeException(e);
      }
    });
  }
}

Then I run the one with Thread.onSpinWait():

@State(Scope.Thread)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
public class ThreadOnSpinWaitBenchmark {
  private final ExecutorService executor = Executors.newFixedThreadPool(1);
  volatile boolean run;

  @Param({"1", "5", "10", "50", "100"})
  long delay;

  @Setup(Level.Invocation)
  public void setUp() {
    run = true;
    startThread();
  }

  @TearDown(Level.Trial)
  public void tearDown() {
    executor.shutdown();
  }

  @Benchmark
  public int onSpinWait() {
    while (run) {
      Thread.onSpinWait();
    }
    return hashCode();
  }

  private void startThread() {
    executor.submit(() -> {
      try {
        Thread.sleep(delay / 2);
        run = false;
      } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
        throw new RuntimeException(e);
      }
    });
  }
}

Both demonstrate nearly same results for delay > 1 ms:

Benchmark                             (delay)  Mode  Cnt   Score    Error  Units

ThreadOnSpinWaitBenchmark.onSpinWait        1  avgt   20   0,003 ±  0,001  ms/op
ThreadOnSpinWaitBenchmark.onSpinWait        5  avgt   20   2,459 ±  0,027  ms/op
ThreadOnSpinWaitBenchmark.onSpinWait       10  avgt   20   5,957 ±  0,064  ms/op
ThreadOnSpinWaitBenchmark.onSpinWait       50  avgt   20  27,915 ±  0,225  ms/op
ThreadOnSpinWaitBenchmark.onSpinWait      100  avgt   20  53,112 ±  0,343  ms/op

ThreadSleep2Benchmark.sleep                 1  avgt   20   1,420 ±  0,043  ms/op
ThreadSleep2Benchmark.sleep                 5  avgt   20   3,183 ±  0,099  ms/op
ThreadSleep2Benchmark.sleep                10  avgt   20   6,723 ±  0,069  ms/op
ThreadSleep2Benchmark.sleep                50  avgt   20  29,697 ±  0,307  ms/op
ThreadSleep2Benchmark.sleep               100  avgt   20  54,730 ±  0,329  ms/op

This is quite expected.

I'd like however to measure CPU load of both approaches. I know that on Linux I can use LinuxPerfNormProfiler but I'm not sure which particular metric I should take to get reliable insight.



No comments:

Post a Comment