#include #include #include #include #include #include #include #include #include #include #define CACHE_HIT_SIZE 1 << 17 using namespace std; size_t size_start = 64; size_t size_end = 16 * (1ull << 20); size_t samples = 2048; size_t size_per_test = 64 * (1ull << 20); size_t tot_sum = 0; size_t delay = 0; float speed = 0; bool dummy = false; void __attribute__((noinline)) memcpy_noinline(void *dst, void *src, size_t size); void __attribute__((noinline)) memset_noinline(void *dst, int value, size_t size); uint64_t __attribute__((noinline)) sum(volatile void *src, size_t size); enum BenchType { MemcpyBench, MemsetBench, SumBench, }; static void usage(char* p) { printf("Usage: %s \n" " is one of the following:\n" " --memcpy\n" " --memset\n" " --sum\n" " are optional and apply to all tests:\n" " --dummy\n" " Simulates cpu-only load of a test. Guaranteed to use L2\n" " instead. Not supported on --sum test.\n" " --delay DELAY_DIVISOR\n" " --start START_SIZE_MB\n" " --end END_SIZE_MB (requires start, optional)\n" " --samples NUM_SAMPLES\n" , p); } int main(int argc, char *argv[]) { BenchType type = MemcpyBench; if (argc <= 1) { usage(argv[0]); return 0; } for (int i = 1; i < argc; i++) { if (string(argv[i]) == string("--memcpy")) { type = MemcpyBench; } else if (string(argv[i]) == string("--memset")) { type = MemsetBench; } else if (string(argv[i]) == string("--sum")) { type = SumBench; } else if (string(argv[i]) == string("--dummy")) { dummy = true; } else if (i + 1 < argc) { if (string(argv[i]) == string("--delay")) { delay = atoi(argv[++i]); } else if (string(argv[i]) == string("--start")) { size_start = atoi(argv[++i]) * (1ull << 20); size_end = size_start; } else if (string(argv[i]) == string("--end")) { size_t end = atoi(argv[++i]) * (1ull << 20); if (end > size_start && i > 3 && string(argv[i-3]) == string("--start")) { size_end = end; } else { printf("Cannot specify --end without --start.\n"); return 0; } } else if (string(argv[i]) == string("--samples")) { samples = atoi(argv[++i]); } else { printf("Unknown argument %s\n", argv[i]); return 0; } } else { printf("The %s option requires a single argument.\n", argv[i]); return 0; } } unique_ptr src(new uint8_t[size_end]); unique_ptr dst(new uint8_t[size_end]); memset(src.get(), 1, size_end); double start_pow = log10(size_start); double end_pow = log10(size_end); double pow_inc = (end_pow - start_pow) / samples; //cout << "src: " << (uintptr_t)src.get() << endl; //cout << "dst: " << (uintptr_t)dst.get() << endl; for (double cur_pow = start_pow; cur_pow <= end_pow && samples > 0; cur_pow += pow_inc) { chrono::time_point copy_start, copy_end, pre_wait; size_t cur_size = (size_t)pow(10.0, cur_pow); size_t iter_per_size = size_per_test / cur_size; // run benchmark switch (type) { case MemsetBench: { memcpy_noinline(src.get(), dst.get(), cur_size); memset_noinline(dst.get(), 0xdeadbeef, cur_size); size_t hit_size = CACHE_HIT_SIZE; copy_start = chrono::high_resolution_clock::now(); for (int i = 0; i < iter_per_size; i++) { if (!dummy) { memset_noinline(dst.get(), 0xdeadbeef, cur_size); } else { while (hit_size < cur_size) { memset_noinline (dst.get(), 0xdeadbeef, CACHE_HIT_SIZE); hit_size += 1 << 17; } } if (delay != 0) this_thread::sleep_for(chrono ::nanoseconds(size_per_test / delay)); } copy_end = chrono::high_resolution_clock::now(); break; } case MemcpyBench: { memcpy_noinline(dst.get(), src.get(), cur_size); memcpy_noinline(src.get(), dst.get(), cur_size); size_t hit_size = CACHE_HIT_SIZE; copy_start = chrono::high_resolution_clock::now(); for (int i = 0; i < iter_per_size; i++) { if (!dummy) { memcpy_noinline(dst.get(), src.get(), cur_size); } else { while (hit_size < cur_size) { memcpy_noinline (dst.get(), src.get(), CACHE_HIT_SIZE); hit_size += CACHE_HIT_SIZE; } } if (delay != 0) this_thread::sleep_for(chrono ::nanoseconds(size_per_test / delay)); } copy_end = chrono::high_resolution_clock::now(); break; } case SumBench: { uint64_t s = 0; s += sum(src.get(), cur_size); copy_start = chrono::high_resolution_clock::now(); for (int i = 0; i < iter_per_size; i++) { s += sum(src.get(), cur_size); if (delay != 0) this_thread::sleep_for(chrono ::nanoseconds(size_per_test / delay)); } copy_end = chrono::high_resolution_clock::now(); tot_sum += s; break; } } samples--; double ns_per_copy = chrono::duration_cast(copy_end - copy_start).count() / double(iter_per_size); double gb_per_sec = ((double)cur_size / (1ull<<30)) / (ns_per_copy / 1.0E9); if (type == MemcpyBench) gb_per_sec *= 2.0; double percent_waiting = 0; if (delay != 0) { percent_waiting = (size_per_test / delay) / ns_per_copy * 100; } cout << "size: " << cur_size << ", perf: " << gb_per_sec << "GB/s, iter: " << iter_per_size << ", \% time spent waiting: " << percent_waiting << endl; } return 0; }