summaryrefslogtreecommitdiff
path: root/vrsite/bench_test.go
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-07-30 17:05:12 -0300
committerGabriel Schneider <[email protected]>2026-07-30 17:56:23 -0300
commit8ec4e135e318c7799b2576a8729493c063da4a97 (patch)
treeab37cdbb7f478eb996264cf941891f127f303c85 /vrsite/bench_test.go
parentcb05c363045bf0e7af5858f6d7bc26f026fa9d70 (diff)
downloadnotevi-8ec4e135e318c7799b2576a8729493c063da4a97.tar.gz
notevi-8ec4e135e318c7799b2576a8729493c063da4a97.zip
Diffstat (limited to 'vrsite/bench_test.go')
-rw-r--r--vrsite/bench_test.go168
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)