// File: /workspace/github_c_makefile_star2_O3/google[P]multichase/pingpong.c
// Function: main
int main(int argc, char **argv) {
  void *(*thread_fn)(void *data) = NULL;
  int c;
  char *p;

  while ((c = getopt(argc, argv, "c:lur:xs:")) != -1) {
    switch (c) {
      case 'l':
        if (thread_fn) goto thread_fn_error;
        thread_fn = locked_loop;
        break;
      case 'u':
        if (thread_fn) goto thread_fn_error;
        thread_fn = unlocked_loop;
        break;
      case 'x':
        if (thread_fn) goto thread_fn_error;
        thread_fn = xadd_loop;
        break;
      case 'r':
        nr_relax = strtoul(optarg, &p, 0);
        if (*p) {
          fprintf(stderr, "-r requires a numeric argument\n");
          exit(1);
        }
        break;
      case 'c':
        nr_tested_cores = strtoul(optarg, &p, 0);
        if (*p) {
          fprintf(stderr, "-c requires a numeric argument\n");
          exit(1);
        }
        break;
      case 's':
        nr_array_elts = strtoul(optarg, &p, 0);
        if (*p) {
          fprintf(stderr, "-s requires a numeric argument\n");
          exit(1);
        }
        if (posix_memalign((void **)&communication_array, 1ull << 21,
                           nr_array_elts * sizeof(*communication_array))) {
          fprintf(stderr, "posix_memalign failed\n");
          exit(1);
        }
        break;
      default:
        fprintf(stderr,
                "usage: %s [-l | -u | -x] [-r nr_relax] [-s "
                "nr_array_elts_to_dirty] [-c nr_tested_cores]\n",
                argv[0]);
        exit(1);
    }
  }
  if (thread_fn == NULL) {
  thread_fn_error:
    fprintf(stderr, "must specify exactly one of -u, -l or -x\n");
    exit(1);
  }

  setvbuf(stdout, NULL, _IONBF, BUFSIZ);

  // find the active cpus
  cpu_set_t cpus;
  if (sched_getaffinity(getpid(), sizeof(cpus), &cpus)) {
    perror("sched_getaffinity");
    exit(1);
  }

  printf(
      "avg latency to communicate a modified line from one core to another\n");
  printf("times are in ns\n\n");

  // print top row header
  const int col_width = 8;
  size_t first_cpu = ~0;
  size_t last_cpu = 0;
  printf("   ");
  for (size_t j = 0; j < CPU_SETSIZE; ++j) {
    if (CPU_ISSET(j, &cpus)) {
      if (first_cpu > j) {
        first_cpu = j;
      } else {
        printf("%*zu", col_width, j);
      }
      if (last_cpu < j) {
        last_cpu = j;
      }
    }
  }
  printf("\n");

  for (size_t i = 0, core = 0; i < last_cpu && core < nr_tested_cores; ++i) {
    if (!CPU_ISSET(i, &cpus)) {
      continue;
    }
    ++core;
    thread_args_t even;
    CPU_ZERO(&even.cpus);
    CPU_SET(i, &even.cpus);
    even.me = 0;
    even.buddy = 1;
    printf("%2zu:", i);
    for (size_t j = first_cpu + 1; j <= i; ++j) {
      if (CPU_ISSET(j, &cpus)) {
        printf("%*s", col_width, "");
      }
    }
    for (size_t j = i + 1; j <= last_cpu; ++j) {
      if (!CPU_ISSET(j, &cpus)) {
        continue;
      }

      thread_args_t odd;
      CPU_ZERO(&odd.cpus);
      CPU_SET(j, &odd.cpus);
      odd.me = 1;
      odd.buddy = 0;
      __sync_lock_test_and_set(&nr_pingpongs.x, 0);
      pthread_t odd_thread;
      if (pthread_create(&odd_thread, NULL, thread_fn, &odd)) {
        perror("pthread_create odd");
        exit(1);
      }
      pthread_t even_thread;
      if (pthread_create(&even_thread, NULL, thread_fn, &even)) {
        perror("pthread_create even");
        exit(1);
      }

      uint64_t last_stamp = now_nsec();
      double best_sample = 1. / 0.;  // infinity
      for (size_t sample_no = 0; sample_no < NR_SAMPLES; ++sample_no) {
        usleep(SAMPLE_US);
        atomic_t s = __sync_lock_test_and_set(&nr_pingpongs.x, 0);
        uint64_t time_stamp = now_nsec();
        double sample = (time_stamp - last_stamp) / (double)s;
        last_stamp = time_stamp;
        if (sample < best_sample) {
          best_sample = sample;
        }
      }
      printf("%*.1f", col_width, best_sample);

      stop_loops = 1;
      if (pthread_join(odd_thread, NULL)) {
        perror("pthread_join odd_thread");
        exit(1);
      }
      if (pthread_join(even_thread, NULL)) {
        perror("pthread_join even_thread");
        exit(1);
      }
      stop_loops = 0;

      if (munmap(pingpong_mutex, getpagesize())) {
        perror("munmap");
        exit(1);
      }
      pingpong_mutex = NULL;
    }
    printf("\n");
  }
  printf("\n");

  return 0;
}

// Assembly:
pushq %rbp
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
subq $0x1f8, %rsp            # imm = 0x1F8
movq %rsi, %r14
movl %edi, %ebp
movq $0x0, 0x8(%rsp)
leaq 0xb371e(%rip), %r15     # 0x4b504c
movq $0x4f0060, %rbx         # imm = 0x4F0060
leaq 0x18(%rsp), %r12
leaq 0xb36ef(%rip), %r13     # 0x4b5030
movl %ebp, %edi
movq %r14, %rsi
movq %r15, %rdx
callq 0x456ca0
cmpl $0x71, %eax
jle 0x40198c
addl $-0x72, %eax
cmpl $0x6, %eax
ja 0x401ea7
movslq (%r13,%rax,4), %rax
addq %r13, %rax
jmpq *%rax
movq (%rbx), %rdi
movq %r12, %rsi
xorl %edx, %edx
callq 0x40b770
movq %rax, 0xe6793(%rip)     # 0x4e8110
movq 0x18(%rsp), %rax
cmpb $0x0, (%rax)
je 0x401941
jmp 0x401ea2
cmpl $0x63, %eax
jne 0x4019fb
movq (%rbx), %rdi
movq %r12, %rsi
xorl %edx, %edx
callq 0x40b770
movq %rax, 0xe6773(%rip)     # 0x4e8118
movq 0x18(%rsp), %rax
cmpb $0x0, (%rax)
je 0x401941
jmp 0x401e9d
movq (%rbx), %rdi
movq %r12, %rsi
xorl %edx, %edx
callq 0x40b770
movq %rax, 0xe8e38(%rip)     # 0x4ea800
movq 0x18(%rsp), %rcx
cmpb $0x0, (%rcx)
jne 0x401e98
shlq $0x3, %rax
movl $0x200000, %esi         # imm = 0x200000
leaq 0xe8e22(%rip), %rdi     # 0x4ea808
movq %rax, %rdx
callq 0x428520
testl %eax, %eax
je 0x401941
jmp 0x401e93
cmpl $-0x1, %eax
je 0x401a44
cmpl $0x6c, %eax
jne 0x401ea7
cmpq $0x0, 0x8(%rsp)
leaq 0x4a3(%rip), %rax       # 0x401eb9
jmp 0x401a34
cmpq $0x0, 0x8(%rsp)
leaq 0x52b(%rip), %rax       # 0x401f50
jmp 0x401a34
cmpq $0x0, 0x8(%rsp)
leaq 0x5b4(%rip), %rax       # 0x401fe8
movq %rax, 0x8(%rsp)
je 0x401941
jmp 0x401eaf
cmpq $0x0, 0x8(%rsp)
je 0x401eaf
movq $0x4e8730, %rax         # imm = 0x4E8730
movq (%rax), %rdi
movl $0x2000, %ecx           # imm = 0x2000
xorl %esi, %esi
movl $0x2, %edx
callq 0x4133a0
callq 0x4555d0
leaq 0x178(%rsp), %rdx
movl $0x80, %esi
movl %eax, %edi
callq 0x456ea0
testl %eax, %eax
jne 0x401eb4
leaq 0xb375e(%rip), %rdi     # 0x4b51f1
callq 0x413200
leaq 0xb3796(%rip), %rdi     # 0x4b5235
callq 0x413200
leaq 0xb36b7(%rip), %rdi     # 0x4b5162
xorl %eax, %eax
callq 0x40c050
movq $-0x1, %rbx
leaq 0xb36a6(%rip), %r14     # 0x4b5166
movq $0x0, 0x10(%rsp)
xorl %r15d, %r15d
movq %r15, %rax
shrq $0x6, %rax
movq 0x178(%rsp,%rax,8), %rax
btq %r15, %rax
jae 0x401b12
movq %r15, %rax
cmpq %r15, %rbx
ja 0x401afe
movq %r14, %rdi
movl $0x8, %esi
movq %r15, %rdx
xorl %eax, %eax
callq 0x40c050
movq %rbx, %rax
movq 0x10(%rsp), %rcx
cmpq %r15, %rcx
cmovbeq %r15, %rcx
movq %rcx, 0x10(%rsp)
movq %rax, %rbx
incq %r15
cmpq $0x400, %r15            # imm = 0x400
jne 0x401acc
movl $0xa, %edi
callq 0x413590
cmpq $0x0, 0x10(%rsp)
setne %cl
movq 0xe65e0(%rip), %rax     # 0x4e8118
testq %rax, %rax
setne %dl
andb %cl, %dl
cmpb $0x1, %dl
jne 0x401e5c
leaq 0x1(%rbx), %rcx
movq %rcx, 0x30(%rsp)
leaq 0x20(%rsp), %r14
xorl %r13d, %r13d
xorl %r8d, %r8d
movq %r13, %rdx
shrq $0x6, %rdx
movq 0x178(%rsp,%rdx,8), %rdi
movl $0x1, %esi
movl %r13d, %ecx
shlq %cl, %rsi
btq %r13, %rdi
jae 0x401e49
incq %r8
movq %r8, 0x38(%rsp)
xorpd %xmm0, %xmm0
movapd %xmm0, 0x160(%rsp)
movapd %xmm0, 0x150(%rsp)
movapd %xmm0, 0x140(%rsp)
movapd %xmm0, 0x130(%rsp)
movapd %xmm0, 0x120(%rsp)
movapd %xmm0, 0x110(%rsp)
movapd %xmm0, 0x100(%rsp)
movapd %xmm0, 0xf0(%rsp)
orq %rsi, 0xf0(%rsp,%rdx,8)
movabsq $0x100000000, %rax      # imm = 0x100000000
movq %rax, 0x170(%rsp)
leaq 0xb3575(%rip), %rdi     # 0x4b516b
movq %r13, %rsi
xorl %eax, %eax
callq 0x40c050
movq %rbx, 0x40(%rsp)
movq %rbx, %r15
movq %r13, %r12
cmpq %r13, 0x30(%rsp)
ja 0x401c4c
incq %r15
movq %r15, %rax
shrq $0x6, %rax
movq 0x178(%rsp,%rax,8), %rax
btq %r15, %rax
jae 0x401c44
leaq 0xb3540(%rip), %rdi     # 0x4b5171
movl $0x8, %esi
leaq 0xcb698(%rip), %rdx     # 0x4cd2d5
xorl %eax, %eax
callq 0x40c050
cmpq %r13, %r15
jb 0x401c12
movq %r13, %r12
incq %r12
movq %r12, %rax
shrq $0x6, %rax
movq 0x178(%rsp,%rax,8), %rsi
movl $0x1, %edx
movl %r12d, %ecx
shlq %cl, %rdx
btq %r12, %rsi
jae 0x401e23
xorpd %xmm0, %xmm0
movapd %xmm0, 0xd0(%rsp)
movapd %xmm0, 0xc0(%rsp)
movapd %xmm0, 0xb0(%rsp)
movapd %xmm0, 0xa0(%rsp)
movapd %xmm0, 0x90(%rsp)
movapd %xmm0, 0x80(%rsp)
movapd %xmm0, 0x70(%rsp)
movapd %xmm0, 0x60(%rsp)
orq %rdx, 0x60(%rsp,%rax,8)
movq $0x1, 0xe0(%rsp)
xorl %eax, %eax
xchgl %eax, 0xe8f2e(%rip)     # 0x4eac00
leaq 0x58(%rsp), %rdi
xorl %esi, %esi
movq 0x8(%rsp), %rbx
movq %rbx, %rdx
leaq 0x60(%rsp), %rcx
callq 0x41d5f0
testl %eax, %eax
jne 0x401e89
leaq 0x50(%rsp), %rdi
xorl %esi, %esi
movq %rbx, %rdx
leaq 0xf0(%rsp), %rcx
callq 0x41d5f0
testl %eax, %eax
jne 0x401e84
movl $0x1, %edi
movq %r14, %rsi
callq 0x4554f0
imulq $0x3b9aca00, 0x20(%rsp), %r15 # imm = 0x3B9ACA00
addq 0x28(%rsp), %r15
movl $0x5, %ebp
movsd 0xb32ce(%rip), %xmm0    # 0x4b5008
movsd %xmm0, 0x48(%rsp)
movl $0x3d090, %edi          # imm = 0x3D090
callq 0x457fe0
xorl %ebx, %ebx
xchgl %ebx, 0xe8eae(%rip)     # 0x4eac00
movl $0x1, %edi
movq %r14, %rsi
callq 0x4554f0
imulq $0x3b9aca00, 0x20(%rsp), %rax # imm = 0x3B9ACA00
addq 0x28(%rsp), %rax
movq %rax, %rcx
subq %r15, %rcx
movq %rcx, %xmm1
punpckldq 0xb3290(%rip), %xmm1 # xmm1 = xmm1[0],mem[0],xmm1[1],mem[1]
subpd 0xb3298(%rip), %xmm1    # 0x4b5020
movapd %xmm1, %xmm0
unpckhpd %xmm1, %xmm0            # xmm0 = xmm0[1],xmm1[1]
xorps %xmm2, %xmm2
cvtsi2sd %rbx, %xmm2
addsd %xmm1, %xmm0
divsd %xmm2, %xmm0
minsd 0x48(%rsp), %xmm0
movq %rax, %r15
decq %rbp
jne 0x401d3a
leaq 0xb33e7(%rip), %rdi     # 0x4b519c
movl $0x8, %esi
movb $0x1, %al
callq 0x40c050
movl $0x1, 0xe9235(%rip)     # 0x4eb000
movq 0x58(%rsp), %rdi
xorl %esi, %esi
callq 0x41e690
testl %eax, %eax
jne 0x401e7f
movq 0x50(%rsp), %rdi
xorl %esi, %esi
callq 0x41e690
testl %eax, %eax
jne 0x401e7a
movl $0x0, 0xe9203(%rip)     # 0x4eb000
movq 0xe9204(%rip), %rbp     # 0x4eb008
callq 0x457f50
movslq %eax, %rsi
movq %rbp, %rdi
callq 0x4580b0
testl %eax, %eax
jne 0x401e8e
movq $0x0, 0xe91e5(%rip) # 0x4eb008
cmpq 0x10(%rsp), %r12
jb 0x401c4c
movl $0xa, %edi
callq 0x413590
movq 0xe62d9(%rip), %rax     # 0x4e8118
movq 0x40(%rsp), %rbx
movq 0x38(%rsp), %r8
incq %r13
cmpq 0x10(%rsp), %r13
jae 0x401e5c
cmpq %rax, %r8
jb 0x401b5d
movl $0xa, %edi
callq 0x413590
xorl %eax, %eax
addq $0x1f8, %rsp            # imm = 0x1F8
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
callq 0x4011e8
callq 0x4011d3
callq 0x4011be
callq 0x4011a9
callq 0x4011fd
callq 0x401125
callq 0x401100
callq 0x40114a
callq 0x40116f
movq %r14, %rdi
callq 0x401237
callq 0x401212
callq 0x401194