diff options
| author | Gabriel Schneider <[email protected]> | 2026-07-30 17:05:12 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-07-30 17:56:23 -0300 |
| commit | 8ec4e135e318c7799b2576a8729493c063da4a97 (patch) | |
| tree | ab37cdbb7f478eb996264cf941891f127f303c85 /vrsite/bench_test.go | |
| parent | cb05c363045bf0e7af5858f6d7bc26f026fa9d70 (diff) | |
| download | notevi-8ec4e135e318c7799b2576a8729493c063da4a97.tar.gz notevi-8ec4e135e318c7799b2576a8729493c063da4a97.zip | |
Diffstat (limited to 'vrsite/bench_test.go')
| -rw-r--r-- | vrsite/bench_test.go | 168 |
1 files changed, 166 insertions, 2 deletions
diff --git a/vrsite/bench_test.go b/vrsite/bench_test.go index 8950b8f..c3eac0c 100644 --- a/vrsite/bench_test.go +++ b/vrsite/bench_test.go @@ -55,14 +55,163 @@ const MAX_ENTRIES: usize = 4096; static GREETING: &str = "hello \"quoted\" <world> & friends"; `) -func synthRust(size int) []byte { +// One chunk per language whose query leans on a predicate, to keep an eye on +// what predicates cost: go's builtin list is an #any-of? (a byte compare per +// call), while python, javascript and C run a casing regex over every +// identifier they see. +var langChunks = []struct { + path string + src []byte +}{ + {"bench.go", []byte(`// Package cache keeps entries with hit counts. +package cache + +import ( + "fmt" + "sync" +) + +// Entry is one cached value and its hit count. +type Entry struct { + Key string + Value []byte + hits uint64 + tags map[string][]byte +} + +const MaxEntries = 4096 + +func NewEntry(key string, value []byte) *Entry { + return &Entry{Key: key, Value: value, tags: make(map[string][]byte)} +} + +// Touch records a hit and returns the running total. +func (e *Entry) Touch(mu *sync.Mutex) uint64 { + mu.Lock() + defer mu.Unlock() + e.hits++ + if e.hits%100 == 0 { + fmt.Printf("entry %q hit %d times \"escaped\"\n", e.Key, e.hits) + } + return e.hits +} + +func (e *Entry) Lookup(keys []string) ([]byte, bool) { + for _, k := range keys { + if v, ok := e.tags[k]; ok && len(v) > 0 { + return v, true + } + } + return nil, false +} +`)}, + {"bench.py", []byte(`"""Module documentation with some text.""" +import sys +from collections import OrderedDict + +MAX_ENTRIES = 4096 + + +class CacheEntry: + """A cached entry with a hit count.""" + + def __init__(self, key, value): + self.key = key + self.value = value + self.hits = 0 + self.tags = OrderedDict() + + @property + def stale(self): + return self.hits > MAX_ENTRIES + + def touch(self): + self.hits += 1 + if self.hits % 100 == 0: + print(f"entry {self.key} hit {self.hits} times \"escaped\"", file=sys.stderr) + return self.hits + + def lookup(self, keys): + for k in keys: + if k in self.tags: + return self.tags[k], True + return None, False +`)}, + {"bench.js", []byte(`// Cache entries with hit counts. +import { EventEmitter } from "node:events"; + +const MAX_ENTRIES = 4096; + +export class CacheEntry extends EventEmitter { + constructor(key, value) { + super(); + this.key = key; + this.value = value; + this.hits = 0; + this.tags = new Map(); + } + + get stale() { + return this.hits > MAX_ENTRIES; + } + + touch() { + this.hits += 1; + if (this.hits % 100 === 0) { + console.log(` + "`entry ${this.key} hit ${this.hits} times \"escaped\"`" + `); + } + return this.hits; + } + + lookup(keys) { + return keys.map((k) => this.tags.get(k)).find((v) => v !== undefined) ?? null; + } +} +`)}, + {"bench.c", []byte(`/* Cache entries with hit counts. */ +#include <stdio.h> +#include <string.h> + +#define MAX_ENTRIES 4096 + +struct cache_entry { + const char *key; + unsigned char *value; + unsigned long hits; +}; + +static struct cache_entry *entry_new(const char *key, unsigned char *value) { + static struct cache_entry e; + e.key = key; + e.value = value; + e.hits = 0; + return &e; +} + +unsigned long entry_touch(struct cache_entry *e) { + e->hits++; + if (e->hits % 100 == 0) { + fprintf(stderr, "entry %s hit %lu times \"escaped\"\n", e->key, e->hits); + } + return e->hits; +} + +int entry_lookup(struct cache_entry *e, const char *key) { + return e->key != NULL && strcmp(e->key, key) == 0; +} +`)}, +} + +func synth(chunk []byte, size int) []byte { var b bytes.Buffer for b.Len() < size { - b.Write(rustChunk) + b.Write(chunk) } return b.Bytes() } +func synthRust(size int) []byte { return synth(rustChunk, size) } + var sizes = []int{128 << 10, 1 << 20, 4 << 20} func benchHL(b *testing.B) *highlighter { @@ -85,6 +234,21 @@ func BenchmarkClassify(b *testing.B) { } } +// the same work for the languages whose queries evaluate predicates +func BenchmarkClassifyLangs(b *testing.B) { + hl := benchHL(b) + for _, c := range langChunks { + src := synth(c.src, 1<<20) + b.Run(c.path, func(b *testing.B) { + b.SetBytes(int64(len(src))) + b.ReportAllocs() + for i := 0; i < b.N; i++ { + hl.classify(c.path, src) + } + }) + } +} + // classify + full per-line HTML emit func BenchmarkRender(b *testing.B) { hl := benchHL(b) |
