Last active
July 22, 2026 23:40
-
-
Save ShanonJackson/bf90cb316c4fab1b8772b4dc8aeb326a to your computer and use it in GitHub Desktop.
x86_64 AMD ZEN 3 Lexing JS at 5-7GB/S (Just boundary finding + tokenization) - Purposely excludes some things for demonstration purposes.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| * SIMD JavaScript/TypeScript lexer for x86-64 AVX2, tuned for AMD Zen 3 <<< IMPORTANT | |
| * | |
| * CccLex L = {0}; | |
| * uint8_t *buf = ccc_source_alloc(n); // 64-byte aligned, 64B zero-padded | |
| * memcpy(buf, bytes, n); | |
| * size_t ntok = ccc_lex(&L, buf, n); // tokens: L.starts[i], L.kinds[i] | |
| * ccc_source_free(buf); | |
| * ccc_free(&L); | |
| * | |
| * src must be 64-byte aligned with 64 zero bytes after src[n-1]; ccc_source_alloc | |
| * and ccc_read_file return a suitable buffer. starts[i] is a byte offset, kinds[i] | |
| * is the enum below. Each whitespace run is one WS token. | |
| * | |
| * cc -O3 -march=znver3 lexer.c -o ccc | |
| */ | |
| #define _POSIX_C_SOURCE 199309L | |
| #include <immintrin.h> | |
| #include <stdbool.h> | |
| #include <stdint.h> | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <time.h> | |
| #include <x86intrin.h> | |
| enum { WS, IDENT, NUM, STR, TMPL, LCOM, BCOM, REGEX, PUNCT }; | |
| static inline void *ccc_alloc64(size_t size) { | |
| #ifdef _WIN32 | |
| return _aligned_malloc(size, 64); | |
| #else | |
| return aligned_alloc(64, (size + 63) & ~(size_t)63); | |
| #endif | |
| } | |
| static inline void ccc_afree(void *p) { | |
| #ifdef _WIN32 | |
| _aligned_free(p); | |
| #else | |
| free(p); | |
| #endif | |
| } | |
| uint8_t *ccc_source_alloc(size_t n) { | |
| uint8_t *b = (uint8_t *)ccc_alloc64(n + 64); | |
| if (b) memset(b + n, 0, 64); | |
| return b; | |
| } | |
| void ccc_source_free(uint8_t *b) { ccc_afree(b); } | |
| static inline bool is_regex_keyword(const uint8_t *p, size_t len) { | |
| switch (len) { | |
| case 2: return !memcmp(p, "in", 2) || !memcmp(p, "of", 2) || !memcmp(p, "do", 2); | |
| case 3: return !memcmp(p, "new", 3); | |
| case 4: return !memcmp(p, "case", 4) || !memcmp(p, "void", 4) || !memcmp(p, "else", 4); | |
| case 5: return !memcmp(p, "yield", 5) || !memcmp(p, "await", 5) || !memcmp(p, "throw", 5); | |
| case 6: return !memcmp(p, "return", 6) || !memcmp(p, "typeof", 6) || !memcmp(p, "delete", 6); | |
| case 10: return !memcmp(p, "instanceof", 10); | |
| default: return false; | |
| } | |
| } | |
| static inline uint64_t bm_lo_mask(size_t b) { | |
| return ~((~(uint64_t)1) << (b & 63)); | |
| } | |
| static inline int64_t bm_prev1(const uint64_t *bm, size_t p) { | |
| if (p == 0) return -1; | |
| size_t i = p - 1; | |
| int64_t w = (int64_t)(i >> 6); | |
| uint64_t x = bm[w] & bm_lo_mask(i); | |
| for (; w >= 0; w--, x = bm[w]) | |
| if (x) return (w << 6) + (63 - (int64_t)__builtin_clzll(x)); | |
| return -1; | |
| } | |
| static inline void bm_set1(uint64_t *bm, size_t i) { bm[i >> 6] |= (uint64_t)1 << (i & 63); } | |
| static inline size_t bm_next0(const uint64_t *bm, size_t i, size_t n) { | |
| size_t w = i >> 6; | |
| uint64_t inv = ~bm[w] & ~((((uint64_t)1 << (i & 63)) - 1)); | |
| while (!inv) { | |
| if ((++w << 6) >= n) return n; | |
| inv = ~bm[w]; | |
| } | |
| size_t r = (w << 6) + (size_t)__builtin_ctzll(inv); | |
| return r < n ? r : n; | |
| } | |
| static inline void bm_clear_range(uint64_t *bm, size_t a, size_t b) { | |
| if (a > b) return; | |
| size_t wa = a >> 6, wb = b >> 6; | |
| uint64_t lo = ~(uint64_t)0 << (a & 63); | |
| uint64_t hi = bm_lo_mask(b); | |
| if (wa == wb) { bm[wa] &= ~(lo & hi); return; } | |
| bm[wa] &= ~lo; | |
| volatile uint64_t *vm = bm; | |
| for (size_t w = wa + 1; w < wb; w++) vm[w] = 0; | |
| bm[wb] &= ~hi; | |
| } | |
| static inline __m256i veq(__m256i v, uint8_t c) { | |
| return _mm256_cmpeq_epi8(v, _mm256_set1_epi8((char)c)); | |
| } | |
| static inline __m256i vwordbits(__m256i v) { | |
| const __m256i lo_tbl = _mm256_setr_epi8( | |
| (char)0xa4, (char)0xa6, (char)0xa6, (char)0xa6, (char)0xb6, (char)0xa6, | |
| (char)0xa6, (char)0xa6, (char)0xa6, (char)0xa6, 0x26, 0x22, 0x22, 0x22, 0x22, 0x2a, | |
| (char)0xa4, (char)0xa6, (char)0xa6, (char)0xa6, (char)0xb6, (char)0xa6, | |
| (char)0xa6, (char)0xa6, (char)0xa6, (char)0xa6, 0x26, 0x22, 0x22, 0x22, 0x22, 0x2a); | |
| const __m256i hi_tbl = _mm256_setr_epi8( | |
| 0x00, 0x00, 0x10, (char)0x80, 0x02, 0x0c, 0x02, 0x04, | |
| 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, | |
| 0x00, 0x00, 0x10, (char)0x80, 0x02, 0x0c, 0x02, 0x04, | |
| 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20); | |
| const __m256i nib = _mm256_set1_epi8(0x0f); | |
| __m256i lo = _mm256_shuffle_epi8(lo_tbl, _mm256_and_si256(v, nib)); | |
| __m256i hi = _mm256_shuffle_epi8(hi_tbl, | |
| _mm256_and_si256(_mm256_srli_epi16(v, 4), nib)); | |
| return _mm256_and_si256(lo, hi); | |
| } | |
| static inline __m256i vws(__m256i v) { | |
| const __m256i t = _mm256_setr_epi8( | |
| 0x20, -128, -128, -128, -128, -128, -128, -128, | |
| -128, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, -128, -128, | |
| 0x20, -128, -128, -128, -128, -128, -128, -128, | |
| -128, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, -128, -128); | |
| return _mm256_cmpeq_epi8(_mm256_shuffle_epi8(t, v), v); | |
| } | |
| static inline __m256i vopener(__m256i v) { | |
| const __m256i t = _mm256_setr_epi8( | |
| 0x60, -128, 0x22, -128, -128, -128, -128, 0x27, | |
| -128, -128, -128, -128, -128, -128, -128, 0x2f, | |
| 0x60, -128, 0x22, -128, -128, -128, -128, 0x27, | |
| -128, -128, -128, -128, -128, -128, -128, 0x2f); | |
| return _mm256_cmpeq_epi8(_mm256_shuffle_epi8(t, v), v); | |
| } | |
| static inline uint32_t mm(__m256i v) { return (uint32_t)_mm256_movemask_epi8(v); } | |
| static inline __m256i load256(const uint8_t *src, size_t i) { | |
| return _mm256_loadu_si256((const __m256i *)(src + i)); | |
| } | |
| static inline __m256i bcast_at(const uint8_t *p) { | |
| #if defined(__GNUC__) | |
| __m256i v; | |
| __asm__("vpbroadcastb %1, %0" : "=x"(v) : "m"(*p)); | |
| return v; | |
| #else | |
| return _mm256_set1_epi8((char)*p); | |
| #endif | |
| } | |
| static inline size_t find_any(const uint8_t *src, size_t n, size_t i, | |
| uint8_t a, uint8_t b, uint8_t c, uint8_t d) { | |
| while (i + 32 <= n) { | |
| __m256i v = load256(src, i); | |
| uint32_t m = mm(_mm256_or_si256(_mm256_or_si256(veq(v, a), veq(v, b)), | |
| _mm256_or_si256(veq(v, c), veq(v, d)))); | |
| if (m) return i + (size_t)__builtin_ctz(m); | |
| i += 32; | |
| } | |
| for (; i < n; i++) { | |
| uint8_t x = src[i]; | |
| if (x == a || x == b || x == c || x == d) return i; | |
| } | |
| return n; | |
| } | |
| static size_t scan_quoted(const uint8_t *src, size_t n, size_t i, uint8_t q) { | |
| __m256i vq = bcast_at(&src[i - 1]); | |
| const __m256i vb = _mm256_set1_epi8('\\'); | |
| (void)q; | |
| for (;;) { | |
| while (i + 32 <= n) { | |
| __m256i v = load256(src, i); | |
| uint32_t mq = mm(_mm256_cmpeq_epi8(v, vq)); | |
| uint32_t mb = mm(_mm256_cmpeq_epi8(v, vb)); | |
| uint32_t m = mq | mb; | |
| if (m) { | |
| uint32_t t = (uint32_t)__builtin_ctz(m); | |
| if ((mb >> t) & 1) { i += t + 2; goto rescan; } | |
| return i + t; | |
| } | |
| i += 32; | |
| } | |
| for (; i < n; i++) { | |
| uint8_t x = src[i]; | |
| if (x == '\\') { i++; continue; } | |
| if (x == q) return i; | |
| } | |
| return n; | |
| rescan:; | |
| } | |
| } | |
| static size_t scan_block(const uint8_t *src, size_t n, size_t i) { | |
| for (;;) { | |
| size_t p = find_any(src, n, i, '/', '/', '/', '/'); | |
| if (p >= n) return n; | |
| if (p > i && src[p - 1] == '*') return p; | |
| i = p + 1; | |
| } | |
| } | |
| static inline size_t scan_block_ops(const uint8_t *src, size_t n, | |
| const uint64_t *op, size_t nb, | |
| size_t w, uint64_t bits, size_t s) { | |
| uint64_t b = bits & (bits - 1); | |
| int miss = 0; | |
| for (;;) { | |
| while (b == 0) { | |
| if (++w >= nb) return n; | |
| b = op[w]; | |
| } | |
| size_t p = (w << 6) + (size_t)__builtin_ctzll(b); | |
| uint16_t pair; | |
| memcpy(&pair, src + p - 1, 2); | |
| if (p > s + 2 && pair == 0x2F2A) return p; | |
| if (++miss == 3) return scan_block(src, n, p + 1); | |
| b &= b - 1; | |
| } | |
| } | |
| static inline size_t find_any_re(const uint8_t *src, size_t n, size_t i) { | |
| const __m256i t = _mm256_setr_epi8( | |
| -128, -128, -128, -128, -128, -128, -128, -128, | |
| -128, -128, -128, 0x5b, 0x5c, 0x5d, -128, 0x2f, | |
| -128, -128, -128, -128, -128, -128, -128, -128, | |
| -128, -128, -128, 0x5b, 0x5c, 0x5d, -128, 0x2f); | |
| while (i + 32 <= n) { | |
| __m256i v = load256(src, i); | |
| uint32_t m = mm(_mm256_cmpeq_epi8(_mm256_shuffle_epi8(t, v), v)); | |
| if (m) return i + (size_t)__builtin_ctz(m); | |
| i += 32; | |
| } | |
| for (; i < n; i++) { | |
| uint8_t x = src[i]; | |
| if (x == '/' || x == '\\' || x == '[' || x == ']') return i; | |
| } | |
| return n; | |
| } | |
| static size_t scan_regex(const uint8_t *src, size_t n, size_t i) { | |
| bool incl = false; | |
| for (;;) { | |
| size_t p = find_any_re(src, n, i); | |
| if (p >= n) return n; | |
| uint8_t c = src[p]; | |
| if (c == '\\') { i = p + 2; continue; } | |
| if (incl) { if (c == ']') incl = false; i = p + 1; continue; } | |
| if (c == '[') { incl = true; i = p + 1; continue; } | |
| if (c == '/') return p; | |
| i = p + 1; | |
| } | |
| } | |
| static inline void classify_block(const uint8_t *src, size_t i, size_t blk, | |
| uint64_t *word, uint64_t *st, uint64_t *op, | |
| uint8_t *kind, uint64_t *pcw, uint64_t *pcs) { | |
| const __m256i v_ws = _mm256_set1_epi8(WS); | |
| const __m256i v_ident = _mm256_set1_epi8(IDENT); | |
| const __m256i v_num = _mm256_set1_epi8(NUM); | |
| const __m256i v_punct = _mm256_set1_epi8(PUNCT); | |
| __m256i v0 = load256(src, i); | |
| __m256i v1 = load256(src, i + 32); | |
| __m256i b0 = vwordbits(v0), b1 = vwordbits(v1); | |
| const __m256i k7f = _mm256_set1_epi8(0x7f); | |
| __m256i w0 = _mm256_add_epi8(b0, k7f), w1 = _mm256_add_epi8(b1, k7f); | |
| __m256i s0 = vws(v0), s1 = vws(v1); | |
| __m256i d0 = b0; | |
| __m256i d1 = b1; | |
| __m256i q0 = vopener(v0), q1 = vopener(v1); | |
| uint64_t wordm = (uint64_t)mm(w0) | ((uint64_t)mm(w1) << 32); | |
| uint64_t wsm = (uint64_t)mm(s0) | ((uint64_t)mm(s1) << 32); | |
| word[blk] = wordm; | |
| op[blk] = (uint64_t)mm(q0) | ((uint64_t)mm(q1) << 32); | |
| uint64_t wprev = (wordm << 1) | *pcw; | |
| uint64_t sprev = (wsm << 1) | *pcs; | |
| *pcw = wordm >> 63; | |
| *pcs = wsm >> 63; | |
| st[blk] = ~((wsm & sprev) | (wordm & wprev)); | |
| __m256i k0 = v_punct; | |
| k0 = _mm256_blendv_epi8(k0, v_ws, s0); | |
| k0 = _mm256_blendv_epi8(k0, v_ident, w0); | |
| k0 = _mm256_blendv_epi8(k0, v_num, d0); | |
| __m256i k1 = v_punct; | |
| k1 = _mm256_blendv_epi8(k1, v_ws, s1); | |
| k1 = _mm256_blendv_epi8(k1, v_ident, w1); | |
| k1 = _mm256_blendv_epi8(k1, v_num, d1); | |
| _mm256_storeu_si256((__m256i *)(kind + blk * 64), k0); | |
| _mm256_storeu_si256((__m256i *)(kind + blk * 64 + 32), k1); | |
| } | |
| static void classify(const uint8_t *src, size_t n, uint64_t *word, uint64_t *st, | |
| uint64_t *op, uint8_t *kind) { | |
| uint64_t cw = 0, cs = 0; | |
| size_t i = 0, blk = 0; | |
| for (; i + 64 <= n; i += 64, blk++) | |
| classify_block(src, i, blk, word, st, op, kind, &cw, &cs); | |
| if (i < n) { | |
| classify_block(src, i, blk, word, st, op, kind, &cw, &cs); | |
| uint64_t mask = ((uint64_t)1 << (n - i)) - 1; | |
| word[blk] &= mask; | |
| op[blk] &= mask; | |
| st[blk] &= mask; | |
| } | |
| } | |
| static bool prev_is_regex(const uint8_t *src, const uint64_t *st, const uint8_t *kind, | |
| const uint64_t *word, size_t n, size_t p) { | |
| for (int64_t q = bm_prev1(st, p); q >= 0; q = bm_prev1(st, (size_t)q)) { | |
| size_t qi = (size_t)q; | |
| uint8_t k = kind[qi]; | |
| if (k == WS || k == LCOM || k == BCOM) continue; | |
| if (k == STR || k == REGEX || k == TMPL || k == NUM) return false; | |
| if (k == IDENT) return is_regex_keyword(src + qi, bm_next0(word, qi, n) - qi); | |
| if (k == PUNCT) return !(src[qi] == ')' || src[qi] == ']'); | |
| return true; | |
| } | |
| return true; | |
| } | |
| static void carve(const uint8_t *src, size_t n, uint64_t *st, uint8_t *kind, | |
| const uint64_t *word, const uint64_t *op) { | |
| size_t nb = (n + 63) >> 6; | |
| for (size_t w = 0; w < nb; w++) { | |
| uint64_t bits = op[w]; | |
| while (bits) { | |
| size_t s = (w << 6) + (size_t)__builtin_ctzll(bits); | |
| uint8_t c = src[s], kd; | |
| size_t end; | |
| if (c == '/') { | |
| uint8_t d = src[s + 1]; | |
| if (d == '/') { | |
| end = find_any(src, n, s + 2, '\n', '\r', '\n', '\r'); | |
| kind[s] = LCOM; | |
| if (end > s + 1) bm_clear_range(st, s + 1, end - 1); | |
| if (end < n) bm_set1(st, end); | |
| goto advance; | |
| } | |
| if (d == '*') { | |
| size_t e = scan_block_ops(src, n, op, nb, w, bits, s); | |
| end = (e < n) ? e + 1 : n; | |
| kd = BCOM; | |
| } else if (prev_is_regex(src, st, kind, word, n, s)) { | |
| size_t e = scan_regex(src, n, s + 1); | |
| end = (e < n) ? e + 1 : n; | |
| if (end < n && ((word[end >> 6] >> (end & 63)) & 1)) | |
| end = bm_next0(word, end, n); | |
| kd = REGEX; | |
| } else { | |
| bits &= bits - 1; | |
| continue; | |
| } | |
| } else { | |
| size_t e = scan_quoted(src, n, s + 1, c); | |
| end = (e < n) ? e + 1 : n; | |
| kd = (c == '`') ? TMPL : STR; | |
| } | |
| kind[s] = kd; | |
| if (end > s + 1) bm_clear_range(st, s + 1, end - 1); | |
| advance: | |
| if (end >= n) return; | |
| size_t we = end >> 6; | |
| if (we == w) { | |
| bits &= ~(uint64_t)0 << (end & 63); | |
| } else { | |
| w = we; | |
| bits = op[w] & (~(uint64_t)0 << (end & 63)); | |
| } | |
| } | |
| } | |
| } | |
| static _Alignas(64) uint8_t ccc_lut0z[256][8]; | |
| static _Alignas(64) uint8_t ccc_lutpad[256][32]; | |
| static void build_pair_luts(void) { | |
| for (size_t m = 0; m < 256; m++) { | |
| size_t k = 0; | |
| memset(ccc_lut0z[m], 0, 8); | |
| memset(ccc_lutpad[m], 0, 32); | |
| for (size_t bit = 0; bit < 8; bit++) | |
| if ((m >> bit) & 1) { | |
| ccc_lut0z[m][k] = (uint8_t)bit; | |
| ccc_lutpad[m][8 + k] = (uint8_t)(bit + 8); | |
| k++; | |
| } | |
| for (size_t j = k; j < 8; j++) ccc_lutpad[m][8 + j] = 0x80; | |
| } | |
| } | |
| static inline __m128i compose_ctrl(unsigned sub0, unsigned sub1, unsigned pc0) { | |
| __m128i row0 = _mm_loadl_epi64((const __m128i *)ccc_lut0z[sub0]); | |
| __m128i row1 = _mm_loadu_si128((const __m128i *)(ccc_lutpad[sub1] + 8 - pc0)); | |
| return _mm_or_si128(row0, row1); | |
| } | |
| static size_t compress(const uint64_t *st, const uint8_t *kind, size_t nb, | |
| uint32_t *starts, uint8_t *kinds) { | |
| const __m256i k16 = _mm256_set1_epi32(16); | |
| size_t m = 0; | |
| for (size_t b = 0; b < nb; b++) { | |
| uint64_t mword = st[b]; | |
| if (mword == 0) continue; | |
| __m256i pb = _mm256_set1_epi32((int)(b * 64)); | |
| __m128i ctrl[4]; | |
| size_t cur[4]; | |
| for (size_t p = 0; p < 4; p++) { | |
| unsigned sub0 = (unsigned)((mword >> (16 * p)) & 0xff); | |
| unsigned sub1 = (unsigned)((mword >> (16 * p + 8)) & 0xff); | |
| unsigned pc0 = (unsigned)__builtin_popcount(sub0); | |
| __m128i c = compose_ctrl(sub0, sub1, pc0); | |
| ctrl[p] = c; | |
| cur[p] = m; | |
| _mm256_storeu_si256((__m256i *)(starts + m), | |
| _mm256_add_epi32(pb, _mm256_cvtepu8_epi32(c))); | |
| _mm256_storeu_si256((__m256i *)(starts + m + 8), | |
| _mm256_add_epi32(pb, _mm256_cvtepu8_epi32(_mm_srli_si128(c, 8)))); | |
| m += pc0 + (unsigned)__builtin_popcount(sub1); | |
| pb = _mm256_add_epi32(pb, k16); | |
| } | |
| for (size_t p = 0; p < 4; p++) { | |
| __m128i kb = _mm_loadu_si128((const __m128i *)(kind + b * 64 + 16 * p)); | |
| _mm_storeu_si128((__m128i *)(kinds + cur[p]), _mm_shuffle_epi8(kb, ctrl[p])); | |
| } | |
| } | |
| return m; | |
| } | |
| typedef struct { | |
| uint32_t *starts; | |
| uint8_t *kinds; | |
| uint64_t *word, *st, *op; | |
| uint8_t *kind; | |
| size_t nb_cap, out_cap; | |
| } CccLex; | |
| void ccc_free(CccLex *L) { | |
| free(L->word); free(L->st); free(L->op); free(L->kind); | |
| free(L->starts); free(L->kinds); | |
| memset(L, 0, sizeof(*L)); | |
| } | |
| static void ccc_ensure(CccLex *L, size_t n) { | |
| size_t nb = (n + 63) / 64 + 1; | |
| if (L->nb_cap < nb) { | |
| L->word = (uint64_t *)realloc(L->word, nb * sizeof(uint64_t)); | |
| L->st = (uint64_t *)realloc(L->st, nb * sizeof(uint64_t)); | |
| L->op = (uint64_t *)realloc(L->op, nb * sizeof(uint64_t)); | |
| L->kind = (uint8_t *)realloc(L->kind, nb * 64); | |
| L->nb_cap = nb; | |
| } | |
| size_t need = n + 16; | |
| if (L->out_cap < need) { | |
| L->starts = (uint32_t *)realloc(L->starts, need * sizeof(uint32_t)); | |
| L->kinds = (uint8_t *)realloc(L->kinds, need); | |
| L->out_cap = need; | |
| } | |
| } | |
| size_t ccc_lex(CccLex *L, const uint8_t *src, size_t n) { | |
| static bool luts = false; | |
| if (!luts) { build_pair_luts(); luts = true; } | |
| if (n == 0) return 0; | |
| size_t nb = (n + 63) / 64; | |
| ccc_ensure(L, n); | |
| classify(src, n, L->word, L->st, L->op, L->kind); | |
| carve(src, n, L->st, L->kind, L->word, L->op); | |
| return compress(L->st, L->kind, nb, L->starts, L->kinds); | |
| } | |
| uint8_t *ccc_read_file(const char *path, size_t *out_len) { | |
| FILE *f = fopen(path, "rb"); | |
| if (!f) return NULL; | |
| fseek(f, 0, SEEK_END); | |
| long sz = ftell(f); | |
| fseek(f, 0, SEEK_SET); | |
| if (sz < 0) { fclose(f); return NULL; } | |
| uint8_t *buf = ccc_source_alloc((size_t)sz); | |
| if (!buf) { fclose(f); return NULL; } | |
| if (fread(buf, 1, (size_t)sz, f) != (size_t)sz) { | |
| fclose(f); ccc_source_free(buf); return NULL; | |
| } | |
| fclose(f); | |
| *out_len = (size_t)sz; | |
| return buf; | |
| } | |
| #ifndef CCC_NO_MAIN | |
| static double now_sec(void) { | |
| struct timespec ts; | |
| clock_gettime(CLOCK_MONOTONIC, &ts); | |
| return (double)ts.tv_sec + (double)ts.tv_nsec * 1e-9; | |
| } | |
| static const char *base_name(const char *p) { | |
| const char *b = p; | |
| for (const char *q = p; *q; q++) | |
| if (*q == '/' || *q == '\\') b = q + 1; | |
| return b; | |
| } | |
| static void bench_one(CccLex *L, const char *name, const uint8_t *src, size_t len) { | |
| int iters = (len < 50000) ? 200 : 30; | |
| for (int i = 0; i < 3; i++) (void)ccc_lex(L, src, len); | |
| uint64_t t0 = __rdtsc(); | |
| double s0 = now_sec(); | |
| size_t total = 0; | |
| for (int i = 0; i < iters; i++) total += ccc_lex(L, src, len); | |
| double s1 = now_sec(); | |
| uint64_t t1 = __rdtsc(); | |
| double bytes = (double)len * iters; | |
| printf(" %-24s %9zu %6.2f %6.2f %9zu\n", name, len, | |
| (double)(t1 - t0) / bytes, bytes / ((s1 - s0) * 1e9), total / (size_t)iters); | |
| } | |
| int main(int argc, char **argv) { | |
| CccLex L; memset(&L, 0, sizeof(L)); | |
| if (argc == 3 && !strcmp(argv[1], "--dump")) { | |
| size_t len = 0; | |
| uint8_t *src = ccc_read_file(argv[2], &len); | |
| if (!src) { fprintf(stderr, "could not read %s\n", argv[2]); return 1; } | |
| size_t nt = ccc_lex(&L, src, len); | |
| for (size_t i = 0; i < nt; i++) printf("%u %u\n", L.starts[i], L.kinds[i]); | |
| ccc_source_free(src); | |
| ccc_free(&L); | |
| return 0; | |
| } | |
| if (argc >= 4 && !strcmp(argv[1], "--spin")) { | |
| double secs = atof(argv[2]); | |
| enum { MAXF = 64 }; | |
| uint8_t *srcs[MAXF]; size_t lens[MAXF]; | |
| int nf = argc - 3; | |
| if (nf > MAXF) nf = MAXF; | |
| for (int i = 0; i < nf; i++) { | |
| srcs[i] = ccc_read_file(argv[3 + i], &lens[i]); | |
| if (!srcs[i]) { fprintf(stderr, "could not read %s\n", argv[3 + i]); return 1; } | |
| } | |
| size_t tok = 0; | |
| for (int i = 0; i < nf; i++) tok += ccc_lex(&L, srcs[i], lens[i]); | |
| double t0 = now_sec(); | |
| uint64_t c0 = __rdtsc(); | |
| double bytes = 0; | |
| size_t rounds = 0; | |
| while (now_sec() - t0 < secs) { | |
| for (int i = 0; i < nf; i++) { tok += ccc_lex(&L, srcs[i], lens[i]); bytes += (double)lens[i]; } | |
| rounds++; | |
| } | |
| uint64_t c1 = __rdtsc(); | |
| double t1 = now_sec(); | |
| printf("spin: %zu rounds %.0f bytes %5.2f cyc/B %7.0f MB/s\n", | |
| rounds, bytes, (double)(c1 - c0) / bytes, bytes / ((t1 - t0) * 1e6)); | |
| for (int i = 0; i < nf; i++) ccc_source_free(srcs[i]); | |
| ccc_free(&L); | |
| (void)tok; | |
| return 0; | |
| } | |
| if (argc == 1) { | |
| fprintf(stderr, | |
| "usage: %s file.js ... benchmark (cyc/B, GB/s)\n" | |
| " %s --dump file.js print the token stream\n" | |
| " %s --spin S file ... round-robin many files for ~S seconds\n", | |
| argv[0], argv[0], argv[0]); | |
| return 2; | |
| } | |
| printf(" %-24s %9s %6s %6s %9s\n", "file", "bytes", "cyc/B", "GB/s", "tokens"); | |
| for (int a = 1; a < argc; a++) { | |
| size_t len = 0; | |
| uint8_t *src = ccc_read_file(argv[a], &len); | |
| if (!src) { fprintf(stderr, " skip %s — could not read\n", argv[a]); continue; } | |
| bench_one(&L, base_name(argv[a]), src, len); | |
| ccc_source_free(src); | |
| } | |
| ccc_free(&L); | |
| return 0; | |
| } | |
| #endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment