13b472e71SThomas Huth #include "qemu/osdep.h" 23b472e71SThomas Huth #include "qemu/thread.h" 33b472e71SThomas Huth #include "qemu/host-utils.h" 43b472e71SThomas Huth #include "qemu/processor.h" 5*5df022cfSPeter Maydell #include "qemu/memalign.h" 63b472e71SThomas Huth 73b472e71SThomas Huth struct thread_info { 83b472e71SThomas Huth uint64_t r; 93b472e71SThomas Huth } QEMU_ALIGNED(64); 103b472e71SThomas Huth 113b472e71SThomas Huth struct count { 123b472e71SThomas Huth QemuMutex lock; 133b472e71SThomas Huth unsigned long val; 143b472e71SThomas Huth } QEMU_ALIGNED(64); 153b472e71SThomas Huth 163b472e71SThomas Huth static QemuThread *threads; 173b472e71SThomas Huth static struct thread_info *th_info; 183b472e71SThomas Huth static unsigned int n_threads = 1; 193b472e71SThomas Huth static unsigned int n_ready_threads; 203b472e71SThomas Huth static struct count *counts; 213b472e71SThomas Huth static unsigned int duration = 1; 223b472e71SThomas Huth static unsigned int range = 1024; 233b472e71SThomas Huth static bool use_mutex; 243b472e71SThomas Huth static bool test_start; 253b472e71SThomas Huth static bool test_stop; 263b472e71SThomas Huth 273b472e71SThomas Huth static const char commands_string[] = 283b472e71SThomas Huth " -n = number of threads\n" 293b472e71SThomas Huth " -m = use mutexes instead of atomic increments\n" 303b472e71SThomas Huth " -p = enable sync profiler\n" 313b472e71SThomas Huth " -d = duration in seconds\n" 323b472e71SThomas Huth " -r = range (will be rounded up to pow2)"; 333b472e71SThomas Huth 343b472e71SThomas Huth static void usage_complete(char *argv[]) 353b472e71SThomas Huth { 363b472e71SThomas Huth fprintf(stderr, "Usage: %s [options]\n", argv[0]); 373b472e71SThomas Huth fprintf(stderr, "options:\n%s\n", commands_string); 383b472e71SThomas Huth } 393b472e71SThomas Huth 403b472e71SThomas Huth /* 413b472e71SThomas Huth * From: https://en.wikipedia.org/wiki/Xorshift 423b472e71SThomas Huth * This is faster than rand_r(), and gives us a wider range (RAND_MAX is only 433b472e71SThomas Huth * guaranteed to be >= INT_MAX). 443b472e71SThomas Huth */ 453b472e71SThomas Huth static uint64_t xorshift64star(uint64_t x) 463b472e71SThomas Huth { 473b472e71SThomas Huth x ^= x >> 12; /* a */ 483b472e71SThomas Huth x ^= x << 25; /* b */ 493b472e71SThomas Huth x ^= x >> 27; /* c */ 503b472e71SThomas Huth return x * UINT64_C(2685821657736338717); 513b472e71SThomas Huth } 523b472e71SThomas Huth 533b472e71SThomas Huth static void *thread_func(void *arg) 543b472e71SThomas Huth { 553b472e71SThomas Huth struct thread_info *info = arg; 563b472e71SThomas Huth 573b472e71SThomas Huth qatomic_inc(&n_ready_threads); 583b472e71SThomas Huth while (!qatomic_read(&test_start)) { 593b472e71SThomas Huth cpu_relax(); 603b472e71SThomas Huth } 613b472e71SThomas Huth 623b472e71SThomas Huth while (!qatomic_read(&test_stop)) { 633b472e71SThomas Huth unsigned int index; 643b472e71SThomas Huth 653b472e71SThomas Huth info->r = xorshift64star(info->r); 663b472e71SThomas Huth index = info->r & (range - 1); 673b472e71SThomas Huth if (use_mutex) { 683b472e71SThomas Huth qemu_mutex_lock(&counts[index].lock); 693b472e71SThomas Huth counts[index].val += 1; 703b472e71SThomas Huth qemu_mutex_unlock(&counts[index].lock); 713b472e71SThomas Huth } else { 723b472e71SThomas Huth qatomic_inc(&counts[index].val); 733b472e71SThomas Huth } 743b472e71SThomas Huth } 753b472e71SThomas Huth return NULL; 763b472e71SThomas Huth } 773b472e71SThomas Huth 783b472e71SThomas Huth static void run_test(void) 793b472e71SThomas Huth { 803b472e71SThomas Huth unsigned int i; 813b472e71SThomas Huth 823b472e71SThomas Huth while (qatomic_read(&n_ready_threads) != n_threads) { 833b472e71SThomas Huth cpu_relax(); 843b472e71SThomas Huth } 853b472e71SThomas Huth 863b472e71SThomas Huth qatomic_set(&test_start, true); 873b472e71SThomas Huth g_usleep(duration * G_USEC_PER_SEC); 883b472e71SThomas Huth qatomic_set(&test_stop, true); 893b472e71SThomas Huth 903b472e71SThomas Huth for (i = 0; i < n_threads; i++) { 913b472e71SThomas Huth qemu_thread_join(&threads[i]); 923b472e71SThomas Huth } 933b472e71SThomas Huth } 943b472e71SThomas Huth 953b472e71SThomas Huth static void create_threads(void) 963b472e71SThomas Huth { 973b472e71SThomas Huth unsigned int i; 983b472e71SThomas Huth 993b472e71SThomas Huth threads = g_new(QemuThread, n_threads); 1003b472e71SThomas Huth th_info = g_new(struct thread_info, n_threads); 1013b472e71SThomas Huth counts = qemu_memalign(64, sizeof(*counts) * range); 1023b472e71SThomas Huth memset(counts, 0, sizeof(*counts) * range); 1033b472e71SThomas Huth for (i = 0; i < range; i++) { 1043b472e71SThomas Huth qemu_mutex_init(&counts[i].lock); 1053b472e71SThomas Huth } 1063b472e71SThomas Huth 1073b472e71SThomas Huth for (i = 0; i < n_threads; i++) { 1083b472e71SThomas Huth struct thread_info *info = &th_info[i]; 1093b472e71SThomas Huth 1103b472e71SThomas Huth info->r = (i + 1) ^ time(NULL); 1113b472e71SThomas Huth qemu_thread_create(&threads[i], NULL, thread_func, info, 1123b472e71SThomas Huth QEMU_THREAD_JOINABLE); 1133b472e71SThomas Huth } 1143b472e71SThomas Huth } 1153b472e71SThomas Huth 1163b472e71SThomas Huth static void pr_params(void) 1173b472e71SThomas Huth { 1183b472e71SThomas Huth printf("Parameters:\n"); 1193b472e71SThomas Huth printf(" # of threads: %u\n", n_threads); 1203b472e71SThomas Huth printf(" duration: %u\n", duration); 1213b472e71SThomas Huth printf(" ops' range: %u\n", range); 1223b472e71SThomas Huth } 1233b472e71SThomas Huth 1243b472e71SThomas Huth static void pr_stats(void) 1253b472e71SThomas Huth { 1263b472e71SThomas Huth unsigned long long val = 0; 1273b472e71SThomas Huth unsigned int i; 1283b472e71SThomas Huth double tx; 1293b472e71SThomas Huth 1303b472e71SThomas Huth for (i = 0; i < range; i++) { 1313b472e71SThomas Huth val += counts[i].val; 1323b472e71SThomas Huth } 1333b472e71SThomas Huth tx = val / duration / 1e6; 1343b472e71SThomas Huth 1353b472e71SThomas Huth printf("Results:\n"); 1363b472e71SThomas Huth printf("Duration: %u s\n", duration); 1373b472e71SThomas Huth printf(" Throughput: %.2f Mops/s\n", tx); 1383b472e71SThomas Huth printf(" Throughput/thread: %.2f Mops/s/thread\n", tx / n_threads); 1393b472e71SThomas Huth } 1403b472e71SThomas Huth 1413b472e71SThomas Huth static void parse_args(int argc, char *argv[]) 1423b472e71SThomas Huth { 1433b472e71SThomas Huth int c; 1443b472e71SThomas Huth 1453b472e71SThomas Huth for (;;) { 1463b472e71SThomas Huth c = getopt(argc, argv, "hd:n:mpr:"); 1473b472e71SThomas Huth if (c < 0) { 1483b472e71SThomas Huth break; 1493b472e71SThomas Huth } 1503b472e71SThomas Huth switch (c) { 1513b472e71SThomas Huth case 'h': 1523b472e71SThomas Huth usage_complete(argv); 1533b472e71SThomas Huth exit(0); 1543b472e71SThomas Huth case 'd': 1553b472e71SThomas Huth duration = atoi(optarg); 1563b472e71SThomas Huth break; 1573b472e71SThomas Huth case 'n': 1583b472e71SThomas Huth n_threads = atoi(optarg); 1593b472e71SThomas Huth break; 1603b472e71SThomas Huth case 'm': 1613b472e71SThomas Huth use_mutex = true; 1623b472e71SThomas Huth break; 1633b472e71SThomas Huth case 'p': 1643b472e71SThomas Huth qsp_enable(); 1653b472e71SThomas Huth break; 1663b472e71SThomas Huth case 'r': 1673b472e71SThomas Huth range = pow2ceil(atoi(optarg)); 1683b472e71SThomas Huth break; 1693b472e71SThomas Huth } 1703b472e71SThomas Huth } 1713b472e71SThomas Huth } 1723b472e71SThomas Huth 1733b472e71SThomas Huth int main(int argc, char *argv[]) 1743b472e71SThomas Huth { 1753b472e71SThomas Huth parse_args(argc, argv); 1763b472e71SThomas Huth pr_params(); 1773b472e71SThomas Huth create_threads(); 1783b472e71SThomas Huth run_test(); 1793b472e71SThomas Huth pr_stats(); 1803b472e71SThomas Huth return 0; 1813b472e71SThomas Huth } 182