Repository navigation
Conversation
|
Warning Review limit reachedYou've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Next included review available in 59 minutes. View limit details
No actionable comments were generated in the recent review. Walkthrough
Merge Risk:
|
922e8b3 to
8a8bf3b
Compare
8a8bf3b to
db8a594
Compare
TypeCheck.checkSingleNode was one match of about 20.8 KB of bytecode. HotSpot never compiles a method over 8000 bytes, since DontCompileHugeMethods is on by default. So it always ran in the interpreter. Every IR node class now carries a tag, and TypeCheck switches on it in two levels. checkSingleNode switches on the node's family and calls one method per family, which switches on the tag. Both levels compile to jump tables. Each case casts the node and reads its fields. The largest method, checkStream, is 4.2 KB. ir-gen puts each node in one family, named where the node is defined: node(Const, "I32", ...). There are 13 families: Const, Core, Struct, Stream, Collection, Agg, IO, NDArray, Random, Relational, Table, Matrix and BlockMatrix. Relational, Table and Matrix hold the nodes a lowering pass removes, and a node goes in the family of the first invariant that forbids it. BlockMatrix is removed at the same point as Table, and takes the nodes that read or make a block matrix but no Table. The other families are buckets that keep each method small, and a node goes in the family of what it produces. A tag holds the family in the bits above Tag.FamilyShift and the node's position in its family below. Within a family, nodes are ordered so the cases TypeCheck and ChildEnv handle sit on consecutive tags, which keeps each switch dense enough to compile to a jump table. ir-gen writes Tag_gen.scala, with the Family and Tag value classes and an Int constant for each family and node. The constants are untyped final vals, so a @switch on them compiles to a jump table. The Table, Matrix and BlockMatrix nodes are written by hand. ir-gen lists them among the generated nodes as handWritten(Table, "TableRead") and generates their tags too. BaseIR takes the tag as a final constructor field, so reading it is a field load. An overridden def tag was a virtual call HotSpot could not inline, and made TypeCheck 11% slower on a 1000-field MakeStruct. Tag stores a Short, which fits in padding most node classes already have. 203 of 217 classes keep their size. Weighted by node visits in the Python test suite, the average node grows from 42.4 to 42.5 bytes. The NDArrayIR trait goes. ir-gen marks those nodes TypedIR[TNDArray] instead, and TypeCheck reads x.typ.elementType. In IRPassBenchmark, against the previous commit, TypeCheck drops from 4.9 ms to 1.0 ms on a 3000-binding Block, from 5.3 ms to 0.58 ms on 2000 nested StreamMaps, from 6.6 ms to 0.92 ms on a balanced tree and from 1.5 ms to 0.26 ms on a 1000-field MakeStruct. The gain comes from getting each method under 8000 bytes. A version that only split the instanceof chain into four methods ran within noise of the tag version over 3 forks. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
db8a594 to
e04d4e7
Compare
Stack 10. Based on #15928.
Why
TypeCheck.checkSingleNodewas onematchof about 20.8 KB of bytecode. HotSpot nevercompiles a method over 8000 bytes (
DontCompileHugeMethods), soTypeCheckalways ran inthe interpreter. A per-node tag fixes that and gives any pass that dispatches on
BaseIRajump table instead of an
instanceofchain.Summary
ir-gen names each node's family where the node is defined, and generates the tags.
Hand-written nodes sit among the generated ones, so their tags follow the same order:
Relational, Table and Matrix hold the nodes a lowering pass removes. A node goes in the
family of the first invariant that forbids it. The other families are buckets that keep
each method small, and a node goes in the family of what it produces.
NoRelationalLetsCompilableIRNoMatrixIR, soLowerMatrixIRremoves the whole familyTypeCheckandChildEnvhandle sit onconsecutive tags. Every
check*method compiles to atableswitch.tagis a final constructor field, so reading it is a field load. An overriddendef tagwas a virtual call HotSpot could not inline, and madeTypeCheck11% slower ona 1000-field
MakeStruct.Tagstores aShort, which fits in padding most node classes already have. 203 of 217classes keep their size. Weighted by node visits in the Python test suite, the average
node grows from 42.4 to 42.5 bytes.
TypeCheckmethod,checkStream, is 4.2 KB.NDArrayIRtrait goes. ir-gen marks those nodesTypedIR[TNDArray]instead.In
IRPassBenchmark, against #15928,TypeCheckdrops from 4.9 ms to 1.0 ms on a3000-binding
Block, from 5.3 ms to 0.58 ms on 2000 nestedStreamMaps, from 6.6 ms to0.92 ms on a balanced tree and from 1.5 ms to 0.26 ms on a 1000-field
MakeStruct. Thegain comes from getting each method under 8000 bytes. A version that only split the
instanceofchain into four methods ran within noise of the tag version over 3 forks.🤖 Generated with Claude Code