diff options
Diffstat (limited to 'research/experiments/044-specs/json_time.c')
| -rw-r--r-- | research/experiments/044-specs/json_time.c | 132 |
1 files changed, 132 insertions, 0 deletions
diff --git a/research/experiments/044-specs/json_time.c b/research/experiments/044-specs/json_time.c new file mode 100644 index 0000000..b401ece --- /dev/null +++ b/research/experiments/044-specs/json_time.c @@ -0,0 +1,132 @@ +/* Research 140, design 027 stage 2a: aup's examples/json.aup (run by Pure) + * against the same validator in C, on the same document: both must say it is + * JSON, then each is timed (the median of seven runs of rounds checks). + * + * cc -O2 -std=c17 -I../../../core json_time.c ../../../build/libpure.a -lm -pthread -o json_time + * ./json_time json.pmod ROUNDS (PURE_JIT and PURE_BATCH choose Pure's path) + * + * The document is json_check.py's: 2,000 objects of a key and an array of a + * number, true, null and a string with an escape. One JSON line. */ +#define _POSIX_C_SOURCE 200809L +#include "module.h" +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +static int space(int b) { return b == 0x20 || b == 0x09 || b == 0x0a || b == 0x0d; } +static int digit(int b) { return b >= 0x30 && b <= 0x39; } +static int hex(int b) { return digit(b) || (b >= 0x61 && b <= 0x66) || (b >= 0x41 && b <= 0x46); } + +/* json.aup's design in C (its UTF-8 check left out: the document is ASCII). */ +static int valid(const unsigned char *d, size_t n, unsigned char *open) { + size_t i = 0, depth = 0; + int mode = 0, ok = 1; + while (ok) { + while (i < n && space(d[i])) ++i; + if (i == n) return mode == 1 && !depth; + int b = d[i]; + if (mode == 1) { + if (!depth) ok = 0; + else if (b == 0x2c) mode = open[depth - 1] == 0x5b ? 0 : 2; + else if (b == open[depth - 1] + 2) --depth; + else ok = 0; + ++i; + } else if ((mode == 3 && b == 0x7d) || (mode == 4 && b == 0x5d)) { + --depth, mode = 1, ++i; + } else if (mode == 2 || mode == 3) { + ok = b == 0x22, mode = 5; + } else if (b == 0x5b || b == 0x7b) { + open[depth++] = (unsigned char)b, mode = b == 0x5b ? 4 : 3, ++i; + } else if (b == 0x22 || mode == 5) { + ++i; + while (i < n && d[i] != 0x22 && ok) { + if (d[i] < 0x20) ok = 0; + else if (d[i] == 0x5c && i + 1 < n && d[i + 1] == 0x75) { + ok = i + 5 < n && hex(d[i + 2]) && hex(d[i + 3]) && hex(d[i + 4]) && hex(d[i + 5]); + i += 6; + } else if (d[i] == 0x5c) { + ok = i + 1 < n && strchr("\"\\/bfnrt", d[i + 1]) && d[i + 1]; + i += 2; + } else ++i; + } + ok = ok && i < n, ++i; + if (mode == 5) { + while (i < n && space(d[i])) ++i; + ok = ok && i < n && d[i] == 0x3a, ++i, mode = 0; + } else mode = 1; + } else if (b == 0x2d || digit(b)) { + if (b == 0x2d) ++i; + ok = i < n && digit(d[i]); + if (ok && d[i] == 0x30) ++i; + else while (i < n && digit(d[i])) ++i; + if (ok && i < n && d[i] == 0x2e) { + ++i, ok = i < n && digit(d[i]); + while (i < n && digit(d[i])) ++i; + } + if (ok && i < n && (d[i] == 0x65 || d[i] == 0x45)) { + ++i; + if (i < n && (d[i] == 0x2b || d[i] == 0x2d)) ++i; + ok = i < n && digit(d[i]); + while (i < n && digit(d[i])) ++i; + } + mode = 1; + } else { + const char *word = b == 0x74 ? "true" : b == 0x66 ? "false" : "null"; + size_t length = strlen(word); + ok = i + length <= n && !memcmp(d + i, word, length), i += length, mode = 1; + } + } + return 0; +} + +static double now_ns(void) { + struct timespec t; + clock_gettime(CLOCK_MONOTONIC, &t); + return (double)t.tv_sec * 1e9 + (double)t.tv_nsec; +} + +static int by_value(const void *a, const void *b) { + double x = *(const double *)a, y = *(const double *)b; + return (x > y) - (x < y); +} + +int main(int argc, char **argv) { + if (argc != 3) { fprintf(stderr, "usage: %s MODULE ROUNDS\n", argv[0]); return 2; } + int rounds = atoi(argv[2]); + static unsigned char doc[1 << 18], open[1 << 18]; + size_t n = 0; + doc[n++] = '['; + for (int k = 0; k < 2000; ++k) + n += (size_t)sprintf((char *)doc + n, "%s{\"key%d\": [1.5e3, true, null, \"text \\u00e9 %d\"]}", k ? "," : "", k, k); + doc[n++] = ']'; + PureModule module; + char error[160]; + if (pure_module_read(argv[1], &module, error, sizeof(error)) != PURE_OK) { fprintf(stderr, "%s\n", error); return 1; } + PureApp app; + if (pure_app_start(&app, &module, (PureData){0}) != PURE_OK) return 1; + int check = pure_module_input(&module, "check"); + if (pure_app_message(&app, (uint32_t)check, NULL, -1, (PureSpan){doc, n}) != PURE_OK) { fprintf(stderr, "event failed\n"); return 1; } + uint64_t work = app.work; + if (app.values[0].num != 1 || !valid(doc, n, open)) { fprintf(stderr, "not both valid\n"); return 1; } + double c_runs[7], pure_runs[7]; + volatile int sink = 0; + for (int r = 0; r < 7; ++r) { + double start = now_ns(); + for (int i = 0; i < rounds; ++i) sink += valid(doc, n, open); + c_runs[r] = (now_ns() - start) / rounds; + start = now_ns(); + for (int i = 0; i < rounds; ++i) + if (pure_app_message(&app, (uint32_t)check, NULL, -1, (PureSpan){doc, n}) != PURE_OK) return 1; + pure_runs[r] = (now_ns() - start) / rounds; + } + qsort(c_runs, 7, sizeof(double), by_value); + qsort(pure_runs, 7, sizeof(double), by_value); + const char *jit = getenv("PURE_JIT"), *batch = getenv("PURE_BATCH"); + printf("{\"size\":%zu,\"jit\":\"%s\",\"batch\":\"%s\",\"c_ns_per_byte\":%.3f,\"pure_ns_per_byte\":%.3f,\"ratio\":%.1f," + "\"instructions\":%llu,\"instructions_per_byte\":%.1f}\n", + n, jit ? jit : "", batch ? batch : "", c_runs[3] / (double)n, pure_runs[3] / (double)n, pure_runs[3] / c_runs[3], + (unsigned long long)work, (double)work / (double)n); + pure_app_stop(&app); + return 0; +} |
