diff options
| author | eunhhu <[email protected]> | 2026-10-19 02:28:34 +0100 |
|---|---|---|
| committer | eunhhu <[email protected]> | 2026-10-19 02:28:34 +0100 |
| commit | ca214af2684b3c6e6982d1fe8be4e7ecc0849b00 (patch) | |
| tree | 9710ca0c2e1671ea03ae17980632bcd5fbf03f6f /research | |
| parent | f12346827a136536c5e8e9c6af3da842bd5cc0c3 (diff) | |
| download | pure-ca214af2684b3c6e6982d1fe8be4e7ecc0849b00.tar.gz pure-ca214af2684b3c6e6982d1fe8be4e7ecc0849b00.zip | |
140's 3, inflate (RFC 1951), written in aup with no new opcode (aup's examples/inflate.aup, codes read a bit at a time from canonical counts as zlib's puff.c): 640 streams zlib made (6,488,480 bytes: random bytes, words, runs and repeats 32,768 back; levels 0, 1, 6, 9 and zlib's five strategies, so stored, fixed and dynamic blocks) inflated to the original's bytes on the three paths, the paths' outputs the same bytes; 240 damaged streams (cut, a bit flipped) read as inflate.py (the Python reference) reads them, 165 refused by both and 75 the same bytes; module_specs in make check: 144 streams on the three paths, 21 cut ones failing and leaving the last bytes; bars: written (no new opcode), three workarounds (lists passed in records: a fn takes no list; the output a list of one: a bytes value is no state; a block's tables built in a fn: a let's list lives in the handler's own block), 132 lines against Python's 81, 1,152 ns an output byte against the same algorithm in C 10.7 (108x) and zlib 3.5 (329x), pure Python 1,214; 191 instructions a byte (a block fn called for each bit was 246 and 1,553 ns: small block fns' inlining is 2c's leftover); the four timing tools' message calls fixed for 4b's API (no keys); CHECKPOINT's direction row
Diffstat (limited to 'research')
| -rw-r--r-- | research/experiments/044-specs/2026-10-19-inflate.jsonl | 5 | ||||
| -rw-r--r-- | research/experiments/044-specs/inflate.py | 109 | ||||
| -rw-r--r-- | research/experiments/044-specs/inflate_check.py | 105 | ||||
| -rw-r--r-- | research/experiments/044-specs/inflate_time.c | 231 | ||||
| -rw-r--r-- | research/experiments/044-specs/json_rec_time.c | 4 | ||||
| -rw-r--r-- | research/experiments/044-specs/json_time.c | 4 | ||||
| -rw-r--r-- | research/experiments/044-specs/json_tree_time.c | 4 | ||||
| -rw-r--r-- | research/experiments/044-specs/time.c | 4 |
8 files changed, 458 insertions, 8 deletions
diff --git a/research/experiments/044-specs/2026-10-19-inflate.jsonl b/research/experiments/044-specs/2026-10-19-inflate.jsonl new file mode 100644 index 0000000..9c85c06 --- /dev/null +++ b/research/experiments/044-specs/2026-10-19-inflate.jsonl @@ -0,0 +1,5 @@ +{"streams": 640, "bytes": 6488480, "right": {"batches": 640, "rounds": 640, "all": 640}, "paths_same": true, "damaged": 240, "damaged_refused": 165, "damaged_agreed": 240, "aup_lines": 132, "aup_brace_lines": 18, "python_lines": 81} +{"size":65536,"stream":22991,"jit":"0","batch":"","c_ns_per_byte":10.657,"zlib_ns_per_byte":3.500,"pure_ns_per_byte":1152.379,"to_c":108.1,"to_zlib":329.3,"instructions":12527836,"instructions_per_byte":191.2} +{"size":65536,"stream":22991,"jit":"0","batch":"0","c_ns_per_byte":10.651,"zlib_ns_per_byte":3.491,"pure_ns_per_byte":1153.844,"to_c":108.3,"to_zlib":330.6,"instructions":12527836,"instructions_per_byte":191.2} +{"size":65536,"stream":22991,"jit":"all","batch":"","c_ns_per_byte":10.646,"zlib_ns_per_byte":3.503,"pure_ns_per_byte":1151.154,"to_c":108.1,"to_zlib":328.7,"instructions":12527836,"instructions_per_byte":191.2} +{"size":65536,"packed":22991,"python_ns_per_byte":1213.5} diff --git a/research/experiments/044-specs/inflate.py b/research/experiments/044-specs/inflate.py new file mode 100644 index 0000000..ac84b20 --- /dev/null +++ b/research/experiments/044-specs/inflate.py @@ -0,0 +1,109 @@ +# inflate (RFC 1951) in plain Python: the reference 140 counts aup's lines against. Codes are read a bit at a time +# from canonical counts (as zlib's puff.c does), so each step is the specification's. +LENGTH_BASE = [3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258] +LENGTH_EXTRA = [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0] +DISTANCE_BASE = [1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, + 4097, 6145, 8193, 12289, 16385, 24577] +DISTANCE_EXTRA = [0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13] +ORDER = [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15] + + +class Bits: + def __init__(self, data): + self.data, self.at = data, 0 + + def take(self, n): + v = 0 + for k in range(n): + v |= (self.data[self.at >> 3] >> (self.at & 7) & 1) << k + self.at += 1 + return v + + +def code(lengths): + """A canonical code: how many codes of each length, and the symbols in code order.""" + counts = [lengths.count(n) for n in range(16)] + return counts, [s for n in range(1, 16) for s in range(len(lengths)) if lengths[s] == n] + + +def decode(bits, counts, symbols): + value = first = index = 0 + for n in range(1, 16): + value |= bits.take(1) + if value - first < counts[n]: + return symbols[index + value - first] + index += counts[n] + first = (first + counts[n]) << 1 + value <<= 1 + raise ValueError('no such code') + + +def inflate(data): + bits, out, last = Bits(data), bytearray(), 0 + while not last: + last, kind = bits.take(1), bits.take(2) + if kind == 0: # stored: from the next byte, its length and the length's complement + at = (bits.at + 7) >> 3 + size = data[at] | data[at + 1] << 8 + if size != (data[at + 2] | data[at + 3] << 8) ^ 0xffff or at + 4 + size > len(data): + raise ValueError('stored length') + out += data[at + 4:at + 4 + size] + bits.at = (at + 4 + size) * 8 + continue + if kind == 1: + lengths, distances = code([8] * 144 + [9] * 112 + [7] * 24 + [8] * 8), code([5] * 30) + elif kind == 2: + literals, count, lengths = bits.take(5) + 257, bits.take(5) + 1, [0] * 19 + for n in range(bits.take(4) + 4): + lengths[ORDER[n]] = bits.take(3) + lengths = code(lengths) + both = [] + while len(both) < literals + count: + symbol = decode(bits, *lengths) + if symbol < 16: + both.append(symbol) + elif symbol == 16: + if not both: + raise ValueError('a repeat with nothing before it') + both += [both[-1]] * (3 + bits.take(2)) + else: + both += [0] * (3 + bits.take(3) if symbol == 17 else 11 + bits.take(7)) + if len(both) > literals + count: + raise ValueError('too many lengths') + lengths, distances = code(both[:literals]), code(both[literals:]) + else: + raise ValueError('block type 3') + while (symbol := decode(bits, *lengths)) != 256: + if symbol < 256: + out.append(symbol) + continue + symbol -= 257 + if symbol >= 29: + raise ValueError('length symbol') + length = LENGTH_BASE[symbol] + bits.take(LENGTH_EXTRA[symbol]) + symbol = decode(bits, *distances) + if symbol >= 30: + raise ValueError('distance symbol') + distance = DISTANCE_BASE[symbol] + bits.take(DISTANCE_EXTRA[symbol]) + if distance > len(out): + raise ValueError('too far back') + for _ in range(length): + out.append(out[-distance]) + return bytes(out) + + +if __name__ == '__main__': + import random, sys, time, zlib + size = int(sys.argv[1]) if len(sys.argv) > 1 else 65536 + chooser = random.Random(140) + words = [bytes(chooser.choices(b'abcdefghijklmnopqrstuvwxyz', k=chooser.randrange(2, 9))) for _ in range(400)] + text = b' '.join(chooser.choice(words) for _ in range(size))[:size] + packer = zlib.compressobj(9, zlib.DEFLATED, -15) + packed = packer.compress(text) + packer.flush() + assert inflate(packed) == text + runs = [] + for _ in range(7): + start = time.perf_counter() + inflate(packed) + runs.append((time.perf_counter() - start) * 1e9 / size) + print('{"size":%d,"packed":%d,"python_ns_per_byte":%.1f}' % (size, len(packed), sorted(runs)[3])) diff --git a/research/experiments/044-specs/inflate_check.py b/research/experiments/044-specs/inflate_check.py new file mode 100644 index 0000000..c3f37b4 --- /dev/null +++ b/research/experiments/044-specs/inflate_check.py @@ -0,0 +1,105 @@ +#!/usr/bin/env python3 +"""Research 140: inflate (RFC 1951) written in aup (aup's examples/inflate.aup). + +Builds it with aup (AUP, default the sibling checkout) and inflates raw deflate streams zlib made (stored, fixed and +dynamic blocks: levels 0, 1, 6 and 9 with each of zlib's five strategies, on random bytes, words, short runs and far +repeats, 0 to 70,000 bytes) on Pure's three paths (batches, rounds, all compiled), each output against the original; +then damaged streams (cut short, a bit flipped) against inflate.py, which reads them the same way: the same bytes when +it takes one, a failed event when it refuses it. Then counts lines (code lines: neither blank nor a comment; brace +lines: a closing brace alone) of inflate.aup and of inflate.py's algorithm (above its main). One JSON line.""" +import json, os, random, subprocess, sys, tempfile, zlib + +here = os.path.dirname(os.path.abspath(__file__)) +pure = os.path.normpath(os.path.join(here, '../../..')) +aup = os.environ.get('AUP', os.path.join(pure, '..', 'aup')) +sys.path.insert(0, here) +import inflate # noqa: E402 + + +def lines(path, stop=None): + code = braces = 0 + for line in open(path): + if stop and line.startswith(stop): break + text = line.strip() + if not text or text.startswith('#'): continue + code += 1 + braces += text == '}' + return code, braces + + +def corpus(chooser): + words = [bytes(chooser.choices(b'abcdefghijklmnopqrstuvwxyz', k=chooser.randrange(2, 9))) for _ in range(400)] + streams = [] + for size in (0, 1, 2, 3, 100, 1000, 10000, 70000): + far = bytes(chooser.randrange(256) for _ in range(min(size, 33000))) + kinds = {'random': bytes(chooser.randrange(256) for _ in range(size)), + 'words': b' '.join(chooser.choice(words) for _ in range(size))[:size], + 'runs': (bytes(chooser.choices(b'ab', k=7)) * size)[:size], + 'far': (far + far[:32768] * 3)[:size]} # repeats 32,768 bytes back at most + for data in kinds.values(): + for level in (0, 1, 6, 9): + for strategy in (zlib.Z_DEFAULT_STRATEGY, zlib.Z_FILTERED, zlib.Z_HUFFMAN_ONLY, zlib.Z_RLE, zlib.Z_FIXED): + packer = zlib.compressobj(level, zlib.DEFLATED, -15, 9, strategy) + streams.append((data, packer.compress(data) + packer.flush())) + return streams + + +def damaged(chooser, streams): + """Streams cut short or with a bit flipped, each with what inflate.py makes of it (None: refused).""" + out = [] + small = [packed for data, packed in streams if 0 < len(packed) < 3000] + for packed in chooser.sample(small, 120): + cut = packed[:chooser.randrange(len(packed))] + flipped = bytearray(packed) + flipped[chooser.randrange(len(packed))] ^= 1 << chooser.randrange(8) + for stream in (cut, bytes(flipped)): + try: + out.append((stream, inflate.inflate(stream))) + except (ValueError, IndexError): + out.append((stream, None)) + return out + + +def run(module, packed_streams, env, work): + given = [] + for n, packed in enumerate(packed_streams): + path = os.path.join(work, f'{n}.deflate') + with open(path, 'wb') as f: + f.write(packed) + given.append(f'message inflate @{path}\n') + out = subprocess.run([os.path.join(pure, 'build', 'pure-run'), module], input=''.join(given), capture_output=True, + text=True, env=dict(os.environ, **env), timeout=1800).stdout.splitlines() + got = [] + for line in out: + state = json.loads(line) + items = state['states']['inflated'] + got.append(bytes.fromhex(items[-1]['data']) if state['fault'] == 'none' and items else None) + return out, got + + +chooser = random.Random(140) +streams = corpus(chooser) +broken = damaged(chooser, streams) +paths = {} +with tempfile.TemporaryDirectory() as work: + module = os.path.join(work, 'inflate.pmod') + subprocess.run([os.path.join(aup, 'build', 'aup'), 'build', os.path.join(aup, 'examples', 'inflate.aup'), '-o', module], + check=True) + outputs = {} + for name, env in (('batches', {'PURE_JIT': '0'}), ('rounds', {'PURE_JIT': '0', 'PURE_BATCH': '0'}), + ('all', {'PURE_JIT': 'all'})): + raw, got = run(module, [packed for _, packed in streams], env, work) + paths[name] = sum(g == data for (data, _), g in zip(streams, got)) if len(got) == len(streams) else 0 + outputs[name] = raw + same_paths = outputs['rounds'] == outputs['batches'] and outputs['all'] == outputs['batches'] + # A damaged stream after a good one: a refused one fails its event and leaves the good one's bytes. + raw, got = run(module, [stream for stream, _ in broken], {}, work) + agreed = sum(g == want or (want is None and g is None) for (_, want), g in zip(broken, got)) if len(got) == len(broken) else 0 + refused = sum(want is None for _, want in broken) +aup_code, aup_braces = lines(os.path.join(aup, 'examples', 'inflate.aup')) +python_code, _ = lines(os.path.join(here, 'inflate.py'), stop='if __name__') +ok = all(n == len(streams) for n in paths.values()) and same_paths and agreed == len(broken) +print(json.dumps({'streams': len(streams), 'bytes': sum(len(d) for d, _ in streams), 'right': paths, 'paths_same': same_paths, + 'damaged': len(broken), 'damaged_refused': refused, 'damaged_agreed': agreed, 'aup_lines': aup_code, + 'aup_brace_lines': aup_braces, 'python_lines': python_code})) +sys.exit(0 if ok else 1) diff --git a/research/experiments/044-specs/inflate_time.c b/research/experiments/044-specs/inflate_time.c new file mode 100644 index 0000000..34cc2d5 --- /dev/null +++ b/research/experiments/044-specs/inflate_time.c @@ -0,0 +1,231 @@ +/* Research 140: inflate (RFC 1951) in aup (aup's examples/inflate.aup, run by Pure) against the same algorithm in C + * (codes read a bit at a time from canonical counts, as zlib's puff.c) and against zlib's inflate (tables), on the + * same raw deflate stream. Each output must be the original's bytes; then each is timed (the median of seven runs of + * rounds inflations). + * + * cc -O2 -std=c17 -I../../../core inflate_time.c ../../../build/libpure.a -lz -lm -pthread -o inflate_time + * ./inflate_time inflate.pmod STREAM ORIGINAL ROUNDS (PURE_JIT and PURE_BATCH choose Pure's path) + * + * One JSON line: the sizes, the nanoseconds per output byte of each, Pure's ratios to both and the instructions Pure + * counted for one stream. */ +#define _POSIX_C_SOURCE 200809L +#include "module.h" +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <zlib.h> + +/* ---- inflate in C, bit by bit (puff's algorithm, no tables) */ +typedef struct { + const unsigned char *in; + size_t size, at; /* at in bits */ + unsigned char *out; + size_t used, room; +} Stream; +typedef struct { short counts[16], symbols[288]; } Code; + +static int take(Stream *s, int n) { + int v = 0; + for (int k = 0; k < n; ++k, ++s->at) { + if (s->at >> 3 >= s->size) return -1; + v |= (s->in[s->at >> 3] >> (s->at & 7) & 1) << k; + } + return v; +} + +static void code(Code *c, const short *lengths, int n) { + memset(c->counts, 0, sizeof(c->counts)); + for (int s = 0; s < n; ++s) c->counts[lengths[s]]++; + short offsets[16] = {0}; + for (int l = 1; l < 15; ++l) offsets[l + 1] = (short)(offsets[l] + c->counts[l]); + for (int s = 0; s < n; ++s) + if (lengths[s]) c->symbols[offsets[lengths[s]]++] = (short)s; +} + +static int decode(Stream *s, const Code *c) { + int value = 0, first = 0, index = 0; + for (int n = 1; n < 16; ++n) { + int b = take(s, 1); + if (b < 0) return -1; + value |= b; + if (value - first < c->counts[n]) return c->symbols[index + value - first]; + index += c->counts[n]; + first = (first + c->counts[n]) << 1; + value <<= 1; + } + return -1; +} + +static const short length_base[29] = {3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, + 99, 115, 131, 163, 195, 227, 258}; +static const short length_extra[29] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0}; +static const short distance_base[30] = {1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, + 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577}; +static const short distance_extra[30] = {0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, + 12, 12, 13, 13}; + +static int put(Stream *s, unsigned char b) { + if (s->used == s->room) return -1; + s->out[s->used++] = b; + return 0; +} + +/* The bytes of a raw deflate stream; -1 if it is none (or the room is short). */ +static long puff(const unsigned char *in, size_t size, unsigned char *out, size_t room) { + Stream s = {in, size, 0, out, 0, room}; + static const short order[19] = {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + int last; + do { + last = take(&s, 1); + int kind = take(&s, 2); + if (last < 0 || kind < 0 || kind == 3) return -1; + if (kind == 0) { + size_t b = (s.at + 7) >> 3; + if (b + 4 > size) return -1; + size_t n = in[b] | in[b + 1] << 8; + if (n != ((in[b + 2] | in[b + 3] << 8) ^ 0xffffu) || b + 4 + n > size || s.used + n > room) return -1; + memcpy(out + s.used, in + b + 4, n); + s.used += n; + s.at = (b + 4 + n) * 8; + continue; + } + Code lc, dc; + short lengths[320]; + if (kind == 1) { + for (int i = 0; i < 288; ++i) lengths[i] = i < 144 ? 8 : i < 256 ? 9 : i < 280 ? 7 : 8; + code(&lc, lengths, 288); + for (int i = 0; i < 30; ++i) lengths[i] = 5; + code(&dc, lengths, 30); + } else { + int literals = take(&s, 5) + 257, count = take(&s, 5) + 1, n = take(&s, 4) + 4; + short small[19] = {0}; + for (int k = 0; k < n; ++k) small[order[k]] = (short)take(&s, 3); + Code c; + code(&c, small, 19); + int got = 0; + while (got < literals + count) { + int symbol = decode(&s, &c); + if (symbol < 0) return -1; + if (symbol < 16) { lengths[got++] = (short)symbol; continue; } + short repeat = 0; + int times; + if (symbol == 16) { + if (!got) return -1; + repeat = lengths[got - 1], times = 3 + take(&s, 2); + } else times = symbol == 17 ? 3 + take(&s, 3) : 11 + take(&s, 7); + if (got + times > literals + count) return -1; + while (times--) lengths[got++] = repeat; + } + code(&lc, lengths, literals); + code(&dc, lengths + literals, count); + } + for (;;) { + int symbol = decode(&s, &lc); + if (symbol < 0) return -1; + if (symbol == 256) break; + if (symbol < 256) { if (put(&s, (unsigned char)symbol)) return -1; continue; } + symbol -= 257; + if (symbol >= 29) return -1; + int length = length_base[symbol] + take(&s, length_extra[symbol]); + symbol = decode(&s, &dc); + if (symbol < 0 || symbol >= 30) return -1; + size_t distance = (size_t)(distance_base[symbol] + take(&s, distance_extra[symbol])); + if (distance > s.used) return -1; + while (length--) if (put(&s, s.out[s.used - distance])) return -1; + } + } while (!last); + return (long)s.used; +} + +static long zlib_inflate(const unsigned char *in, size_t size, unsigned char *out, size_t room) { + z_stream z = {0}; + if (inflateInit2(&z, -15) != Z_OK) return -1; + z.next_in = (unsigned char *)in, z.avail_in = (uInt)size, z.next_out = out, z.avail_out = (uInt)room; + int r = inflate(&z, Z_FINISH); + long used = (long)z.total_out; + inflateEnd(&z); + return r == Z_STREAM_END ? used : -1; +} + +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); +} + +static unsigned char *whole(const char *path, size_t *size) { + FILE *f = fopen(path, "rb"); + if (!f) return NULL; + fseek(f, 0, SEEK_END); + *size = (size_t)ftell(f); + rewind(f); + unsigned char *bytes = malloc(*size ? *size : 1); + if (fread(bytes, 1, *size, f) != *size) { free(bytes); bytes = NULL; } + fclose(f); + return bytes; +} + +int main(int argc, char **argv) { + if (argc != 5) { fprintf(stderr, "usage: %s MODULE STREAM ORIGINAL ROUNDS\n", argv[0]); return 2; } + size_t size, original_size; + unsigned char *stream = whole(argv[2], &size), *original = whole(argv[3], &original_size); + int rounds = atoi(argv[4]); + if (!stream || !original) { fprintf(stderr, "cannot read the stream or the original\n"); return 1; } + unsigned char *out = malloc(original_size + 1); + if (puff(stream, size, out, original_size) != (long)original_size || memcmp(out, original, original_size)) { + fprintf(stderr, "C's bytes differ\n"); + return 1; + } + if (zlib_inflate(stream, size, out, original_size + 1) != (long)original_size || memcmp(out, original, original_size)) { + fprintf(stderr, "zlib's bytes differ\n"); + return 1; + } + + PureModule module = {0}; + 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 input = pure_module_input(&module, "inflate"), state = pure_module_state(&module, "inflated"); + uint64_t before = app.work; + if (pure_app_message(&app, (uint32_t)input, (PureSpan){stream, size}) != PURE_OK) { + fprintf(stderr, "event failed\n"); + return 1; + } + uint64_t work = app.work - before; + const PureList *list = app.lists[state]; + PureCell cell = pure_list_count(list) == 1 ? pure_list_get(list, 0, 0) : (PureCell){0}; + if (cell.size != original_size || memcmp(cell.text, original, original_size)) { fprintf(stderr, "Pure's bytes differ\n"); return 1; } + + double c_runs[7], zlib_runs[7], pure_runs[7]; + volatile unsigned char sink = 0; + for (int r = 0; r < 7; ++r) { + double start = now_ns(); + for (int i = 0; i < rounds; ++i) { puff(stream, size, out, original_size); sink ^= out[i % (original_size + 1)]; } + c_runs[r] = (now_ns() - start) / rounds; + start = now_ns(); + for (int i = 0; i < rounds; ++i) { zlib_inflate(stream, size, out, original_size + 1); sink ^= out[i % (original_size + 1)]; } + zlib_runs[r] = (now_ns() - start) / rounds; + start = now_ns(); + for (int i = 0; i < rounds; ++i) + if (pure_app_message(&app, (uint32_t)input, (PureSpan){stream, size}) != PURE_OK) return 1; + pure_runs[r] = (now_ns() - start) / rounds; + } + qsort(c_runs, 7, sizeof(double), by_value); + qsort(zlib_runs, 7, sizeof(double), by_value); + qsort(pure_runs, 7, sizeof(double), by_value); + double n = (double)original_size; + const char *jit = getenv("PURE_JIT"), *batch = getenv("PURE_BATCH"); + printf("{\"size\":%zu,\"stream\":%zu,\"jit\":\"%s\",\"batch\":\"%s\",\"c_ns_per_byte\":%.3f,\"zlib_ns_per_byte\":%.3f," + "\"pure_ns_per_byte\":%.3f,\"to_c\":%.1f,\"to_zlib\":%.1f,\"instructions\":%llu,\"instructions_per_byte\":%.1f}\n", + original_size, size, jit ? jit : "", batch ? batch : "", c_runs[3] / n, zlib_runs[3] / n, pure_runs[3] / n, + pure_runs[3] / c_runs[3], pure_runs[3] / zlib_runs[3], (unsigned long long)work, (double)work / n); + pure_app_stop(&app); + return 0; +} diff --git a/research/experiments/044-specs/json_rec_time.c b/research/experiments/044-specs/json_rec_time.c index 0d5b225..6c599f5 100644 --- a/research/experiments/044-specs/json_rec_time.c +++ b/research/experiments/044-specs/json_rec_time.c @@ -79,7 +79,7 @@ int main(int argc, char **argv) { 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){(const unsigned char *)doc, n}) != PURE_OK) { + if (pure_app_message(&app, (uint32_t)check, (PureSpan){(const unsigned char *)doc, n}) != PURE_OK) { fprintf(stderr, "event failed\n"); return 1; } @@ -104,7 +104,7 @@ int main(int argc, char **argv) { 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){(const unsigned char *)doc, n}) != PURE_OK) return 1; + if (pure_app_message(&app, (uint32_t)check, (PureSpan){(const unsigned char *)doc, n}) != PURE_OK) return 1; pure_runs[r] = (now_ns() - start) / rounds; } qsort(c_runs, 7, sizeof(double), by_value); diff --git a/research/experiments/044-specs/json_time.c b/research/experiments/044-specs/json_time.c index b4b2eae..1e747c1 100644 --- a/research/experiments/044-specs/json_time.c +++ b/research/experiments/044-specs/json_time.c @@ -106,7 +106,7 @@ int main(int argc, char **argv) { 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; } + if (pure_app_message(&app, (uint32_t)check, (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]; @@ -117,7 +117,7 @@ int main(int argc, char **argv) { 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; + if (pure_app_message(&app, (uint32_t)check, (PureSpan){doc, n}) != PURE_OK) return 1; pure_runs[r] = (now_ns() - start) / rounds; } qsort(c_runs, 7, sizeof(double), by_value); diff --git a/research/experiments/044-specs/json_tree_time.c b/research/experiments/044-specs/json_tree_time.c index 0ec4bec..636eed4 100644 --- a/research/experiments/044-specs/json_tree_time.c +++ b/research/experiments/044-specs/json_tree_time.c @@ -79,7 +79,7 @@ int main(int argc, char **argv) { 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){(const unsigned char *)doc, n}) != PURE_OK) { + if (pure_app_message(&app, (uint32_t)check, (PureSpan){(const unsigned char *)doc, n}) != PURE_OK) { fprintf(stderr, "event failed\n"); return 1; } @@ -104,7 +104,7 @@ int main(int argc, char **argv) { 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){(const unsigned char *)doc, n}) != PURE_OK) return 1; + if (pure_app_message(&app, (uint32_t)check, (PureSpan){(const unsigned char *)doc, n}) != PURE_OK) return 1; pure_runs[r] = (now_ns() - start) / rounds; } qsort(c_runs, 7, sizeof(double), by_value); diff --git a/research/experiments/044-specs/time.c b/research/experiments/044-specs/time.c index f296ece..a196aba 100644 --- a/research/experiments/044-specs/time.c +++ b/research/experiments/044-specs/time.c @@ -91,7 +91,7 @@ int main(int argc, char **argv) { int hash = pure_module_input(&module, "hash"); char want[65]; sha256(data, size, want); - if (pure_app_message(&app, (uint32_t)hash, NULL, -1, (PureSpan){data, size}) != PURE_OK) { fprintf(stderr, "event failed\n"); return 1; } + if (pure_app_message(&app, (uint32_t)hash, (PureSpan){data, size}) != PURE_OK) { fprintf(stderr, "event failed\n"); return 1; } uint64_t work = app.work; Hex got = {{0}, 0}; pure_draft_read(app.texts[0], 0, pure_draft_size(app.texts[0]), gather, &got); @@ -105,7 +105,7 @@ int main(int argc, char **argv) { c_runs[r] = (now_ns() - start) / rounds; start = now_ns(); for (int i = 0; i < rounds; ++i) - if (pure_app_message(&app, (uint32_t)hash, NULL, -1, (PureSpan){data, size}) != PURE_OK) return 1; + if (pure_app_message(&app, (uint32_t)hash, (PureSpan){data, size}) != PURE_OK) return 1; pure_runs[r] = (now_ns() - start) / rounds; } qsort(c_runs, 7, sizeof(double), by_value); |
