perf(tools): linear duplicate-key scan keyed on SimpleFold orbit
PR #66 review round 2: the pairwise EqualFold loop was quadratic, 7s of CPU for ~90k keys under the 1 MiB body cap. foldKey maps each rune to the smallest member of its unicode.SimpleFold orbit, equal exactly when strings.EqualFold is, so the scan is a map lookup again. The scaling test fails on the previous code (7.1s). Refs #65 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
package tools
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// foldKey must agree with strings.EqualFold, which is how encoding/json matches.
|
||||
func TestFoldKeyAgreesWithEqualFold(t *testing.T) {
|
||||
pairs := [][2]string{{"branch", "Branch"}, {"sha", "ſha"}, {"k", "K"}, {"ss", "ß"}, {"a", "b"}, {"Σ", "ς"}}
|
||||
for _, p := range pairs {
|
||||
if got, want := foldKey(p[0]) == foldKey(p[1]), strings.EqualFold(p[0], p[1]); got != want {
|
||||
t.Errorf("%q vs %q: foldKey equal=%v, EqualFold=%v", p[0], p[1], got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The duplicate scan is linear in the number of keys: 90k keys (about the
|
||||
// 1 MiB body cap) must scan well under a second.
|
||||
func TestRejectAmbiguousKeysScalesLinearly(t *testing.T) {
|
||||
var b strings.Builder
|
||||
b.WriteString("{")
|
||||
for i := 0; i < 90_000; i++ {
|
||||
if i > 0 {
|
||||
b.WriteString(",")
|
||||
}
|
||||
fmt.Fprintf(&b, `"k%d":1`, i)
|
||||
}
|
||||
b.WriteString("}")
|
||||
var dst struct {
|
||||
Owner string `json:"owner"`
|
||||
}
|
||||
start := time.Now()
|
||||
if err := rejectAmbiguousKeys([]byte(b.String()), &dst); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if d := time.Since(start); d > 2*time.Second {
|
||||
t.Errorf("scan of 90k keys took %v", d)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user