You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
190 lines
6.8 KiB
190 lines
6.8 KiB
#include <iostream> |
|
#include <chrono> |
|
#include <vector> |
|
#include <algorithm> |
|
#include <numeric> |
|
#include <stdlib.h> |
|
#include <memory> |
|
#include <cmath> |
|
#include <string> |
|
#include <thread> |
|
|
|
#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 <test> <options>\n" |
|
"<test> is one of the following:\n" |
|
" --memcpy\n" |
|
" --memset\n" |
|
" --sum\n" |
|
"<options> 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<uint8_t[]> src(new uint8_t[size_end]); |
|
unique_ptr<uint8_t[]> 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<chrono::high_resolution_clock> |
|
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<chrono::nanoseconds>(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; |
|
}
|
|
|