// File: romkatv[P]action-chain/src/action_chain_test.cc
// Function: int romkatv::(anonymous namespace)::Benchmark<romkatv::ActionChain>(romkatv::(anonymous namespace)::Flags const&)
int Benchmark(const Flags& flags) {
  const std::uint64_t actions_per_thread = flags.actions / flags.threads;
  CHECK(actions_per_thread * flags.threads == flags.actions);

  auto PrintCol = [](const char* name, const auto& val) {
    std::cout << name << '=' << std::setw(17) << std::setprecision(3) << std::left << val;
  };

  PrintCol("sync", flags.sync);
  PrintCol("threads", flags.threads);
  PrintCol("ops-per-action", flags.ops_per_action);
  std::cout << std::flush;

  volatile std::uint64_t counter = 0;
  auto wall_time_start = std::chrono::high_resolution_clock::now();
  double cpu_time_start = CpuTimeSec();
  {
    std::mutex m;
    Sync sync;
    std::vector<std::thread> threads;
    for (std::uint64_t i = 0; i != flags.threads; ++i) {
      threads.emplace_back([&] {
        typename Sync::Mem mem;
        for (std::uint64_t i = 0; i != actions_per_thread; ++i) {
          sync.Run(&mem, [&] {
            for (std::uint64_t j = 0; j != flags.ops_per_action; ++j) ++counter;
          });
        }
      });
    }
    for (std::thread& t : threads) t.join();
  }
  double cpu_time_end = CpuTimeSec();
  auto wall_time_end = std::chrono::high_resolution_clock::now();

  if (counter != flags.ops_per_action * flags.actions) {
    std::cerr << "TEST FAILURE" << std::endl;
    return 1;
  }

  double wall = std::chrono::duration<double>(wall_time_end - wall_time_start).count();
  double cpu = cpu_time_end - cpu_time_start;
  PrintCol("total-wall-time(s)", wall);
  PrintCol("wall-time-per-action(ns)", 1e9 * wall / flags.actions);
  PrintCol("cpu-time-per-action(ns)", 1e9 * cpu / flags.actions);
  std::cout << std::endl;

  return 0;
}

// Assembly:
pushq %rbp
movq %rsp, %rbp
subq $0x160, %rsp            # imm = 0x160
movq %rdi, -0x10(%rbp)
movq -0x10(%rbp), %rax
movq 0x30(%rax), %rax
movq -0x10(%rbp), %rcx
xorl %edx, %edx
divq 0x20(%rcx)
movq %rax, -0x18(%rbp)
movq -0x18(%rbp), %rax
movq -0x10(%rbp), %rcx
imulq 0x20(%rcx), %rax
movq -0x10(%rbp), %rcx
cmpq 0x30(%rcx), %rax
je 0x3b8c
movq 0x1046d(%rip), %rax     # 0x13fd0
movq (%rax), %rdi
leaq 0x960d(%rip), %rsi      # 0xd17a
leaq 0x9617(%rip), %rdx      # 0xd18b
movl $0xa6, %ecx
leaq 0x97ad(%rip), %r8       # 0xd32d
movb $0x0, %al
callq 0x3320
callq 0x3140
jmp 0x3b8e
movq -0x10(%rbp), %rdx
leaq -0x19(%rbp), %rdi
leaq 0x9638(%rip), %rsi      # 0xd1d5
callq 0x5350
movq -0x10(%rbp), %rdx
addq $0x20, %rdx
leaq -0x19(%rbp), %rdi
leaq 0x9625(%rip), %rsi      # 0xd1da
callq 0x53e0
movq -0x10(%rbp), %rdx
addq $0x28, %rdx
leaq -0x19(%rbp), %rdi
leaq 0x9615(%rip), %rsi      # 0xd1e2
callq 0x53e0
movq 0x103e7(%rip), %rdi     # 0x13fc0
movq 0x103e8(%rip), %rsi     # 0x13fc8
callq 0x3270
movq $0x0, -0x28(%rbp)
callq 0x3030
movq %rax, -0x30(%rbp)
callq 0x5470
movsd %xmm0, -0x38(%rbp)
leaq -0x60(%rbp), %rdi
callq 0xa2c0
leaq -0x68(%rbp), %rdi
callq 0xa2e0
leaq -0x80(%rbp), %rdi
callq 0xa330
movq $0x0, -0x88(%rbp)
movq -0x88(%rbp), %rax
movq -0x10(%rbp), %rcx
cmpq 0x20(%rcx), %rax
je 0x3cb4
leaq -0x18(%rbp), %rax
movq %rax, -0xa8(%rbp)
leaq -0x68(%rbp), %rax
movq %rax, -0xa0(%rbp)
movq -0x10(%rbp), %rax
movq %rax, -0x98(%rbp)
leaq -0x28(%rbp), %rax
movq %rax, -0x90(%rbp)
leaq -0x80(%rbp), %rdi
leaq -0xa8(%rbp), %rsi
callq 0x5520
jmp 0x3c75
jmp 0x3c77
movq -0x88(%rbp), %rax
addq $0x1, %rax
movq %rax, -0x88(%rbp)
jmp 0x3c26
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0xb0(%rbp)
movl %eax, -0xb4(%rbp)
leaq -0x80(%rbp), %rdi
callq 0xa430
leaq -0x68(%rbp), %rdi
callq 0xa490
jmp 0x3f20
leaq -0x80(%rbp), %rax
movq %rax, -0xc0(%rbp)
movq -0xc0(%rbp), %rdi
callq 0xa350
movq %rax, -0xc8(%rbp)
movq -0xc0(%rbp), %rdi
callq 0xa380
movq %rax, -0xd0(%rbp)
leaq -0xc8(%rbp), %rdi
leaq -0xd0(%rbp), %rsi
callq 0xa3b0
testb $0x1, %al
jne 0x3cfe
jmp 0x3d2f
leaq -0xc8(%rbp), %rdi
callq 0xa3f0
movq %rax, -0xd8(%rbp)
movq -0xd8(%rbp), %rdi
callq 0x33b0
jmp 0x3d1f
jmp 0x3d21
leaq -0xc8(%rbp), %rdi
callq 0xa410
jmp 0x3ce5
leaq -0x80(%rbp), %rdi
callq 0xa430
leaq -0x68(%rbp), %rdi
callq 0xa490
callq 0x5470
movsd %xmm0, -0xe0(%rbp)
callq 0x3030
movq %rax, -0xe8(%rbp)
movq -0x28(%rbp), %rax
movq -0x10(%rbp), %rcx
movq 0x28(%rcx), %rcx
movq -0x10(%rbp), %rdx
imulq 0x30(%rdx), %rcx
cmpq %rcx, %rax
je 0x3da2
movq 0x10275(%rip), %rdi     # 0x13ff0
leaq 0x95df(%rip), %rsi      # 0xd361
callq 0x3240
movq %rax, %rdi
movq 0x10217(%rip), %rsi     # 0x13fa8
callq 0x3270
movl $0x1, -0x4(%rbp)
jmp 0x3f14
leaq -0xe8(%rbp), %rdi
leaq -0x30(%rbp), %rsi
callq 0xa4d0
movq %rax, -0x100(%rbp)
leaq -0xf8(%rbp), %rdi
movq %rdi, -0x158(%rbp)
leaq -0x100(%rbp), %rsi
callq 0xa520
movq -0x158(%rbp), %rdi
callq 0xa560
movsd %xmm0, -0xf0(%rbp)
movsd -0xe0(%rbp), %xmm0
movsd -0x38(%rbp), %xmm1
subsd %xmm1, %xmm0
movsd %xmm0, -0x108(%rbp)
leaq 0x9567(%rip), %rsi      # 0xd36e
leaq -0x19(%rbp), %rdi
movq %rdi, -0x150(%rbp)
leaq -0xf0(%rbp), %rdx
callq 0x55a0
movq -0x150(%rbp), %rdi
movsd -0xf0(%rbp), %xmm0
movsd 0x91d3(%rip), %xmm1     # 0xd008
movsd %xmm1, -0x148(%rbp)
mulsd %xmm1, %xmm0
movq -0x10(%rbp), %rax
movq 0x30(%rax), %xmm1
movaps 0x91cf(%rip), %xmm2     # 0xd020
movaps %xmm2, -0x140(%rbp)
punpckldq %xmm2, %xmm1    # xmm1 = xmm1[0],xmm2[0],xmm1[1],xmm2[1]
movapd 0x91cc(%rip), %xmm2     # 0xd030
movaps %xmm2, -0x130(%rbp)
subpd %xmm2, %xmm1
movaps %xmm1, %xmm2
unpckhpd %xmm1, %xmm1            # xmm1 = xmm1[1,1]
addsd %xmm2, %xmm1
divsd %xmm1, %xmm0
movsd %xmm0, -0x110(%rbp)
leaq 0x94f4(%rip), %rsi      # 0xd381
leaq -0x110(%rbp), %rdx
callq 0x55a0
movsd -0x148(%rbp), %xmm1
movaps -0x140(%rbp), %xmm3
movaps -0x130(%rbp), %xmm2
movsd -0x108(%rbp), %xmm0
mulsd %xmm1, %xmm0
movq -0x10(%rbp), %rax
movq 0x30(%rax), %xmm1
punpckldq %xmm3, %xmm1    # xmm1 = xmm1[0],xmm3[0],xmm1[1],xmm3[1]
subpd %xmm2, %xmm1
movaps %xmm1, %xmm2
unpckhpd %xmm1, %xmm1            # xmm1 = xmm1[1,1]
addsd %xmm2, %xmm1
divsd %xmm1, %xmm0
movsd %xmm0, -0x118(%rbp)
leaq -0x19(%rbp), %rdi
leaq 0x94ac(%rip), %rsi      # 0xd39a
leaq -0x118(%rbp), %rdx
callq 0x55a0
movq 0x100bf(%rip), %rdi     # 0x13fc0
movq 0x100a0(%rip), %rsi     # 0x13fa8
callq 0x3270
movl $0x0, -0x4(%rbp)
movl -0x4(%rbp), %eax
addq $0x160, %rsp            # imm = 0x160
popq %rbp
retq
movq -0xb0(%rbp), %rdi
callq 0x33d0
nopl (%rax)