|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <wchar.h>
|
|
#ifdef _WIN32
|
|
#include <windows.h>
|
|
#else
|
|
#include <time.h>
|
|
#endif
|
|
|
|
typedef void *(*cpy_t)(void *, const void *, size_t);
|
|
typedef void *(*set_t)(void *, int, size_t);
|
|
typedef int (*cmp_t)(const void *, const void *, size_t);
|
|
typedef void *(*chr_t)(const void *, int, size_t);
|
|
typedef size_t (*len_t)(const char *);
|
|
typedef size_t (*nlen_t)(const char *, size_t);
|
|
typedef char *(*schr_t)(const char *, int);
|
|
typedef char *(*sstr_t)(const char *, const char *);
|
|
typedef int (*scmp_t)(const char *, const char *);
|
|
typedef int (*sncmp_t)(const char *, const char *, size_t);
|
|
typedef size_t (*wlen_t)(const wchar_t *);
|
|
|
|
enum kind { CPY, SET, CMP, CHR, LEN, NLEN, SCHR, SRCHR, SSTR, SCMP, SNCMP, WLEN };
|
|
struct fn { const char *name; enum kind kind; };
|
|
static const struct fn fns[] = {
|
|
{"memcpy", CPY}, {"memmove", CPY}, {"memset", SET}, {"memcmp", CMP}, {"memchr", CHR},
|
|
{"strlen", LEN}, {"strnlen", NLEN}, {"strchr", SCHR}, {"strrchr", SRCHR}, {"strstr", SSTR},
|
|
{"strcmp", SCMP}, {"strncmp", SNCMP}, {"wcslen", WLEN},
|
|
};
|
|
#define NFNS (sizeof(fns) / sizeof(fns[0]))
|
|
|
|
static double now(void) {
|
|
#ifdef _WIN32
|
|
LARGE_INTEGER f, t;
|
|
QueryPerformanceFrequency(&f);
|
|
QueryPerformanceCounter(&t);
|
|
return (double)t.QuadPart / f.QuadPart;
|
|
#else
|
|
struct timespec ts;
|
|
clock_gettime(CLOCK_MONOTONIC, &ts);
|
|
return ts.tv_sec + ts.tv_nsec / 1e9;
|
|
#endif
|
|
}
|
|
|
|
#define BARRIER(p) __asm__ volatile("" : : "r"(p) : "memory")
|
|
|
|
static unsigned char *A, *B;
|
|
static wchar_t *W;
|
|
|
|
/* returns GB/s, or -1 on a wrong result */
|
|
static double bench(enum kind k, void *f, size_t n) {
|
|
long iters = (long)((256ULL << 20) / n);
|
|
size_t bad = 0;
|
|
/* setup: n bytes of content; strings end with NUL at n-1 */
|
|
memset(A, 'a', n);
|
|
memset(B, 'a', n);
|
|
A[n - 2] = '\n';
|
|
A[n - 1] = 0;
|
|
B[n - 2] = '\n';
|
|
B[n - 1] = 0;
|
|
if (k == SRCHR) A[0] = 'z';
|
|
if (k == SSTR) { A[n - 4] = 'x'; A[n - 3] = 'y'; A[n - 2] = 'z'; }
|
|
if (k == CHR) { A[n - 1] = '\n'; A[n - 2] = 'a'; }
|
|
for (size_t i = 0; i < n / sizeof(wchar_t); i++) W[i] = L'a';
|
|
W[n / sizeof(wchar_t) - 1] = 0;
|
|
double t0 = now();
|
|
for (long i = 0; i < iters; i++) {
|
|
BARRIER(A);
|
|
BARRIER(B);
|
|
switch (k) {
|
|
case CPY: bad += ((cpy_t)f)(B, A, n) != B; break;
|
|
case SET: bad += ((set_t)f)(B, 'x', n) != B; break;
|
|
case CMP: bad += ((cmp_t)f)(A, B, n) != 0; break;
|
|
case CHR: bad += (unsigned char *)((chr_t)f)(A, '\n', n) != A + n - 1; break;
|
|
case LEN: bad += ((len_t)f)((char *)A) != n - 1; break;
|
|
case NLEN: bad += ((nlen_t)f)((char *)A, n) != n - 1; break;
|
|
case SCHR: bad += ((schr_t)f)((char *)A, '\n') != (char *)A + n - 2; break;
|
|
case SRCHR: bad += ((schr_t)f)((char *)A, 'z') != (char *)A; break;
|
|
case SSTR: bad += ((sstr_t)f)((char *)A, "xyz") != (char *)A + n - 4; break;
|
|
case SCMP: bad += ((scmp_t)f)((char *)A, (char *)B) != 0; break;
|
|
case SNCMP: bad += ((sncmp_t)f)((char *)A, (char *)B, n) != 0; break;
|
|
case WLEN: bad += ((wlen_t)f)(W) != n / sizeof(wchar_t) - 1; break;
|
|
}
|
|
}
|
|
double t = now() - t0;
|
|
if (bad) return -1;
|
|
return (double)n * iters / t / 1e9;
|
|
}
|
|
|
|
int main(void) {
|
|
size_t sizes[] = {64, 1040, 65536, 1 << 20};
|
|
A = malloc(1 << 20);
|
|
B = malloc(1 << 20);
|
|
W = malloc(1 << 20);
|
|
#ifdef _WIN32
|
|
const char *libs[] = {"ucrtbase.dll", "msvcrt.dll", "ntdll.dll", "vcruntime140.dll"};
|
|
int nlibs = 4;
|
|
#else
|
|
const char *libs[] = {"glibc"};
|
|
int nlibs = 1;
|
|
void *glibc[] = {(void *)memcpy, (void *)memmove, (void *)memset, (void *)memcmp, (void *)memchr,
|
|
(void *)strlen, (void *)strnlen, (void *)strchr, (void *)strrchr, (void *)strstr,
|
|
(void *)strcmp, (void *)strncmp, (void *)wcslen};
|
|
#endif
|
|
printf("%-9s %-17s %10s %10s %10s %10s (GB/s)\n", "func", "library", "64 B", "1040 B", "64 KB", "1 MB");
|
|
for (size_t i = 0; i < NFNS; i++) {
|
|
for (int l = 0; l < nlibs; l++) {
|
|
#ifdef _WIN32
|
|
HMODULE h = LoadLibraryA(libs[l]);
|
|
void *f = h ? (void *)GetProcAddress(h, fns[i].name) : NULL;
|
|
#else
|
|
void *f = glibc[i];
|
|
#endif
|
|
printf("%-9s %-17s", fns[i].name, libs[l]);
|
|
if (!f) { printf(" (not exported)\n"); continue; }
|
|
for (int s = 0; s < 4; s++) {
|
|
double g = bench(fns[i].kind, f, sizes[s]);
|
|
if (g < 0) printf(" %10s", "WRONG");
|
|
else printf(" %10.1f", g);
|
|
}
|
|
printf("\n");
|
|
}
|
|
}
|
|
return 0;
|
|
}
|