Repository navigation
perf(compiler): mark struct values with a string property - #550
Merged
Merged
Conversation
The constructor of every struct class sets the struct value marker, which was a symbol-keyed property. Since struct fields became own properties of the instance, setting a symbol key on the new object made making a struct about ten times slower under JavaScriptCore (bun, Safari): a struct return in a loop went from 3.2 to 40 microseconds. V8 is not affected. The marker is now the string property __goStructValue, which the constructors set by name. As a string key it is enumerable, so ownKeys, Object.keys without it, replaces Object.keys where the keys of a struct are its fields: DeepEqual, struct equality and map key hashing, printing, fmt, encoding/json, maphash, unique and structural type matching. Signed-off-by: Randall C. O'Reilly <oreilly@ucdavis.edu>
This was referenced Oct 25, 2026
Member
|
Landed on master as eefc23f, rebased onto the batch #532 to #550, with regenerated fixtures in fca8071. Removed RuntimeHelperStructValueMarker, which the emitter no longer uses, folded the double map in fmt's object formatting into one, and made compareMapKeys (from #548) compare struct keys by ownKeys, so the string marker is not compared as a field. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Making a struct got about ten times slower under JavaScriptCore (bun, and Safari) with ef7791d, which stores struct fields as own properties of the instance. The cause is the struct value marker that every generated constructor sets, a symbol-keyed property: on a new object with own field properties, a symbol-keyed store takes a slow path in JavaScriptCore. In base/goscript/gsbench of Cogent Core, LadderStructReturn went from 3,246 to 40,114 ns/op under bun between ec20c61 and ef7791d. V8 (node, Chrome) is not affected. Writing the marker under a literal string name restores the speed; a computed key, even a string one, does not.
The marker is now the string __goStructValue, which the emitter writes by name in the constructor of each struct class. A string key is enumerable, so builtin gains ownKeys, which is Object.keys without the marker, and it replaces Object.keys where the keys of a struct are taken as its fields: reflect.DeepEqual, struct equality, the hashing of struct map keys, print, fmt's struct and object formatting, encoding/json's fallback field list and plain object encoding, hash/maphash, unique, structural type matching and the forwarding of struct views. isEmptyObject skips it.
With this, under bun: LadderStructReturn 40,000 -> 3,166 ns/op, StructCopy 17 -> about 1, StructValueMethod 8.5 -> 3.5, LadderStructValue 11,000 -> 5,200, LadderPointerMethod 13,556 -> 5,330, LadderStructPointer 11,016 -> 5,569, BytesBufferPointer 352 -> 180; the whole gsbench suite is about 10% faster by geometric mean.
The compliance suite, typecheck and vitest pass; the regenerated fixture outputs change only in how the constructors set the marker.