Changelog
All notable changes to this project are documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning. Versions apply to the whole repository and are git tags (see docs/release.md). Kits are not released separately and carry no version of their own.
[Unreleased]
-
Docs guides (en + zh): plain-docs pass on performance guides — drop How-to-read / Scope-and-method coaching lists where the lead already says measure-first; replace heuristic-dogfood scope wording with lint-hit vs wall-time framing; prefer
Lint: id (default …)lines; soften coaching bold; sync zh-CN guide-src (and zperf handzh-CN/zig-performance-guide.md) to the English structure and voice. -
Docs zh-CN: site Markdown Chinese coverage for shared docs, kit guides (
guide-src/zh-CN+ build-guides stem pairing), package landings, corpus/evidence-policy/rule-ids, active evidence notes, and root pages published as*.zh.mdviazh-CN/parents. Tombstone evidence underevidence/_removed/intentionally left English-only. -
Docs SEO / GEO (en + zh): description, Open Graph (
og:locale+alternateLocale), Twitter Card, canonical and hreflang; static export forces blocking metadata into HTML (htmlLimitedBots);sitemap.xmlpairs en/zh with language alternates;robots.txtallows common AI crawlers;llms.txt+llms.zh.txt; locale-aware JSON-LD (WebSite/WebPage/SoftwareApplication); post-build sets<html lang="zh-CN">under/zh/for crawlers; 1200×630og.png. README opens with an English-only value-prop hook. -
Docs site: page links no longer keep a trailing
.md(that 404s on GitHub Pages). Assemble rewrites them to trailing-slash directory URLs relative to the page directory, fails the build if any.mdhrefs remain, emits static files as root-absolute paths so the/zh/locale works, and publishesLICENSE/NOTICEas directories sotrailingSlashcan open them. -
All kits: default matching is
--scope allwhere the kit CLI supports it (opt in to--scope loopfor the former in-loop-only match). Finding messages no longer imply non-loop sites are out of scope. Rule slugs containingin-loopwere renamed to*-hot(including leftoverrs-map-new-in-loop→rs-map-new-hot; see per-kit Renamed tables); old slugs are tombstoned under each kitevidence/_removed/*.REMOVED.mdand must never be reused. See each kitdocs/rule-ids.mdRenamed table. jsperf also keeps adopter FP cuts + new hints from earlier commits on this branch. No severity escalations. -
jsperf-lint: default
--scope allreports former in-loop smells everywhere (extreme-perf default); opt in to--scope loopfor the old in-loop-only match. Finding messages and docs no longer imply non-loop sites are out of scope. Still includes loop-header / nested-function FP cuts; path skips fore2e/,playwright-report/, and*.test.*/*.spec.*; new hintsjp-query-all-star,jp-execjs-in-timer, andjp-clone-per-event(stay hint); scanner reads JS insideexecuteJavaScript/evaluatetemplates. No severity escalations;jp-string-plus-in-loopstays tombstoned. -
Documentation site uses a Fumadocs layout: sidebar, command search, and Geist. The pages are assembled with Node.
-
GitHub Pages site for the guides and kit docs, built from the Markdown already in the tree.
-
Removed the checked-in C# bench executable and its .NET build outputs.
-
Restored
rs-map-new-in-loopas hint. Two quiet rustc 1.99 runs of 20000 one-key inserts were about 1.53× and 1.51× versus clearing one map. The same two runs left the largeVecfill at about 1.00× and the largeStringfill at about 1.03×, so those removals stay. -
Quiet reruns on current toolchains. Rust 1.99:
Instant::nowabout 20× (the old 439× after-side was 0.008 ms),File::openabout 416×,Regex::newonregex1.13.1 about 963×. All stay hint. Nim 2.2.12&=versusjoinwas about 0.79×;nm-string-amp-in-loopis removed and the slug is not reused. -
Hint text for compile, parse, open, and clock rules now states the bench premise: hoist only when the pattern, text, path, or timestamp stays the same. A call whose argument is the loop binding is not reported.
Date/Stopwatch/Instant::nowinside a loop thatawaits is not reported in C#, Swift, and Rust. ArkTS no longer reportsJSON.parseon afor/forEach/maphead. ElixirEnum.map/filtersays the cost grows with length. -
JavaScript
jp-string-plus-in-loopis removed.pushthenjoinwas not about 2× versus+=on a short literal,String.fromCharCode, or a 64-character literal (at 22000 charactersjoinwas slower).jp-map-filter-chainnow has JSONL and stays hint: 35000 rows about 4.6× and 9.8×, a 3-row filter+map 1.28×. Hint lines state the bench premise.readFileSyncin aforiterable,Date.nowin a loop thatawaits, andnew RegExp/JSON.parse/ a DOM selector /Intl.DateTimeFormatwhose argument is the loop binding are not reported. -
Measured the previously unbenched C and Swift shapes on this machine.
strlenin a condition was about 125× a hoisted length when the string might change, and about 0.93× when-O2already hoists it (cp-strlen-in-cond, still hint).strcatof fixed chunks was about 221× an end pointer (cp-strcat-hot, stays off). SwiftreserveCapacitywas about 5× on many small arrays and about 1.6× on one large fill (sp-array-no-reserve, still hint).printto/dev/nullwas about 10× aString(sp-print-hot, stays off). Removedsp-string-plus-in-loop(reserve was slower),sp-map-joined-chain(about 1.2×), andsp-force-unwrap-in-loop(same time asif let). -
Removed measured-near-1× matchers:
at-object-delete(~1.01× on a Node proxy),cx-string-append-unreserved(~1.09× time, capacity under 1.4×),rs-string-plus-in-loop(~1.00× on a large fill),rs-vec-no-capacity(~1.00× time and ~1.05× bytes on a large fill), andzp-extern-struct-padding(~1.21× walk, 24 bytes versus 16). Slugs are tombstoned and not reused.format!rebind, value-class allocation, andallocastack growth stay. -
Zig hint:
allocPrint/Allocator.printof an unchanged format string and arguments was about 50× on the general-purpose allocator, about 12× on an arena, and about 14× for a constant string (zp-alloc-print-hot).bufPrint,Writer.print, byte copies, and a 256-byte struct copy stayed at or under about 7× and were not added. Empty-bufferwriteBytewas about 130×, but the same call on a 4KiB buffer was about 1.2×, so it was not added. Default stays hint. -
C++ hints:
const std::string/const std::vectorby value was about 15–21× versus a const reference when the copy allocates (cx-const-by-value). A 4-character string was about 1× and is still hinted. Walkingvector<bool>was about 12× avector<unsigned char>(cx-vector-bool); the packed container is smaller. Hoistingstd::functionwas slower (about 0.6×). Copyingshared_ptrstayed under 7×.map::operator[]on missing keys was about 30× but the same spelling on existing keys was slower thanfind, so it was not added. Defaults stay hint. -
C hints:
allocain a loop covers the stack (20000 × 64 bytes versus one 64-byte buffer) while the touch time stays about 1× (cp-alloca-hot). A 512-byte struct passed by value across two translation units was about 12.3× versus a pointer (cp-struct-by-value). The same shape in one translation unit was about 1×.fgetc/fputc/putcwere faster as a block read (about 24–120×) but were not added: a line matcher cannot tell a byte parser from a bulk read. Defaults stay hint. -
Cross-language check of file opens and clock samples. New hints, each with JSONL, where the same shape was costly and the standard library can reuse a handle or a sample: Python
open(~409×), JavaScriptreadFileSync(~480×),Date.now(~26×), andperformance.now(~19×), Goos.Open(~2300×) andtime.Now(~104×), RubyFile.read(~346×), C#File.ReadAllText(~1560×) andStopwatch.GetTimestamp(~15×), RustFile::open(~2750×) andInstant::now(~439×), SwiftcontentsOfFile(~414×), Objective-CstringWithContentsOfFile(~450×). C#new Regex(now matchescs-regex-hot(~7×, both sides still match). Pythonperf_counter_ns(~2.9×), RubyProcess.clock_gettime(~2.3×),getattr(~1.4×),send(~1.9×), and JavaScriptReflect.get(~8×) were checked and not added. Defaults stay hint. -
C++
ifstreamof one one-byte file was about 2025× versus opening once (cx-ifstream-hot, hint). Zig 0.17openFileAbsolutewas about 2134× andClock.awake.nowabout 42× (zp-open-file-hot,zp-clock-hot, hint). C#GetFieldplusGetValueeach iteration was about 62× versus a direct field (cs-field-by-name-hot, hint). Zigjson.parseFromSliceof one small object was about 2683× versus parsing once (zp-json-hot, hint). Objective-CvalueForKey:was about 124× and Swiftvalue(forKey:)about 35× versus a direct field (oc-value-for-key-hot,sp-value-for-key-hot, hint). Fair rerun of SwiftDate()(~244×) and Objective-C[NSDate date](~769×), with the after loop kept alive. Both are hints with JSONL. A discarded first run had deleted that loop. -
Fair reruns: removed
cx-sstream-in-loop,pp-regex-compile-in-loop,rs-map-new-in-loop,pp-datetime-format-in-loop,gp-time-parse-in-loop, andrb-date-parse-in-loop(near 1×, or the only win was skipping the call).cx-endl-hotis about 23× on an external stream and about 0.9× on a string stream.gp-reflect-hotnow matches name lookup andreflect.New;TypeOfwas about 1.3× and is not matched.cp-regex-hot,cx-clock-hot,cs-json-hot,rs-regex-new-hot,jp-dom-query-hot, andjp-intl-datetimeformat-hothave JSONL and stay hint. A JSON or date value that changes every iteration cannot be hoisted. JavaSimpleDateFormatwas not added: no JDK in this environment. -
Hint rules for repeated compile, parse, query, format, and fresh-map work in Python, JavaScript, Go, C++, C#, Ruby, and Rust. Paired fixtures only. No JSONL, so none of them is
error. The 2026-10-22 corpus verdict does not cover these slugs. -
C hints for
sprintf/fopen/ clock reads in a loop, and for four or more full-width boolean flags in one struct (cp-wide-bool-flags). Paired fixtures only. No JSONL. Not corpus-triaged. -
Swift hints for constructing a date formatter, a JSON decoder, or
NSRegularExpressionin a loop. Both the Swift scanner and the text fallback carry them. Paired fixtures only. No JSONL. Not corpus-triaged. -
Objective-C hints for allocating a date formatter, calling
NSJSONSerialization, or buildingNSRegularExpressionin a loop. Paired fixtures only. No JSONL. Not corpus-triaged. -
Microbench JSONL for the new hint rules that could be timed on one shape (compile/parse/format/copy). Defaults stay hint.
cx-endl-hotandgp-reflect-hotwere about 1×.jp-dom-query-hotandrs-regex-new-hotwere not timed.
[0.1.0] — 2026-10-22
Umbrella first public-shaped snapshot (Asia/Shanghai).
- 19 language kits under
packages/*, corpus-ready under AGENT.md G3. - Rule IDs are semantic slugs only (
{family}-{smell}); numeric codes removed (no aliases). - Measured-useless rules deleted (tombstones), not left as zombie
off. - Root README slimmed; kit status →
docs/kits.md; run commands →docs/quickstart.md. - ~124 active slugs at tag time; see each kit
docs/rule-ids.md.