// Synthetic benchmark for __tsan_read/write{1,2,4,8}. // As compared to mini_bench_local/shared.cc this benchmark passes through // deduplication logic (ContainsSameAccess). // First argument is access size (1, 2, 4, 8). Second optional arg switches // from writes to reads. #include <pthread.h> #include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <linux/futex.h> #include <sys/syscall.h> #include <sys/time.h> template<typename T, bool write> void* thread(void *arg) { const int kSize = 2 << 10; static volatile long data[kSize]; static volatile long turn; const int kRepeat = 1 << 17; const int id = !!arg; for (int i = 0; i < kRepeat; i++) { for (;;) { int t = __atomic_load_n(&turn, __ATOMIC_ACQUIRE); if (t == id) break; syscall(SYS_futex, &turn, FUTEX_WAIT, t, 0, 0, 0); } for (int j = 0; j < kSize; j++) { if (write) { ((volatile T*)&data[j])[0] = 1; ((volatile T*)&data[j])[sizeof(T) == 8 ? 0 : 1] = 1; } else { T v0 = ((volatile T*)&data[j])[0]; T v1 = ((volatile T*)&data[j])[sizeof(T) == 8 ? 0 : 1]; (void)v0; (void)v1; } } __atomic_store_n(&turn, 1 - id, __ATOMIC_RELEASE); syscall(SYS_futex, &turn, FUTEX_WAKE, 0, 0, 0, 0); } return 0; } template<typename T, bool write> void test() { pthread_t th; pthread_create(&th, 0, thread<T, write>, (void*)1); thread<T, write>(0); pthread_join(th, 0); } template<bool write> void testw(int size) { switch (size) { case 1: return test<char, write>(); case 2: return test<short, write>(); case 4: return test<int, write>(); case 8: return test<long long, write>(); } } int main(int argc, char** argv) { int size = 8; bool write = true; if (argc > 1) { size = atoi(argv[1]); if (size != 1 && size != 2 && size != 4 && size != 8) size = 8; } if (argc > 2) write = false; printf("%s%d\n", write ? "write" : "read", size); if (write) testw<true>(size); else testw<false>(size); return 0; } |