Extract Matrix[A] layout fields into Layout value class (Phase 1 + 2) - #109
Conversation
Co-authored-by: Quafadas <24899792+Quafadas@users.noreply.github.com>
Matrix[A] layout fields into Layout value class (Phase 1 + 2)
Bytecode audit (Tier 1) —
|
| threshold | value | provenance |
|---|---|---|
MaxTrivialSize |
6 | discovered |
MaxInlineSize |
35 | discovered |
FreqInlineSize |
325 | discovered |
MaxInlineLevel |
15 | discovered |
InlineSmallCode |
2500 | discovered |
NodeCountInliningCutoff |
18000 | assumed |
HugeMethodLimit |
8000 | assumed |
-XX:HugeMethodLimit= was rejected on the command line: a develop flag compiled out of this product build, so 8000 is taken from the HotSpot source and cannot be confirmed against the running JVM.
| metric | now | baseline | delta |
|---|---|---|---|
| cheatsheet methods | 97 | — | — |
| total bytes | 51103 | 46714 | +9.4% |
| distinct library ops | 178 | 177 | +0.6% |
| bytes per op | 287.1 | 263.9 | +8.8% |
| severity | check | at | method | detail |
|---|---|---|---|---|
| WARN | C1 | cheatsheet.scala:118 |
CheatsheetTest$.matrixRangeSlicing |
6996 bytes, past 68% of HugeMethodLimit=8000 (assumed) |
Annotated methods (47)
| method | annotations | bytes | budget used | loop | at |
|---|---|---|---|---|---|
vecxt.NDArrayFloatOps$.compareGeneral |
@HotPath |
195 | 60% of 325 | yes | ndarrayFloatOps.scala:67 |
vecxt.NDArrayFloatOps$.binaryOpGeneral |
@HotPath |
195 | 60% of 325 | yes | ndarrayFloatOps.scala:20 |
vecxt.NDArrayIntOps$.compareGeneral |
@HotPath |
186 | 57% of 325 | yes | ndarrayIntOps.scala:67 |
vecxt.NDArrayIntOps$.binaryOpGeneral |
@HotPath |
186 | 57% of 325 | yes | ndarrayIntOps.scala:19 |
vecxt.NDArrayDoubleOps$.compareGeneral |
@HotPath |
186 | 57% of 325 | yes | ndarrayDoubleOps.scala:74 |
vecxt.NDArrayDoubleOps$.binaryOpGeneral |
@HotPath |
186 | 57% of 325 | yes | ndarrayDoubleOps.scala:24 |
vecxt.doublearrays$.clamp$bang |
@AllocFree @HotPath |
180 | 55% of 325 | yes | doublearrays.scala:818 |
vecxt.floatarrays$.clamp$bang |
@AllocFree @HotPath |
172 | 53% of 325 | yes | floatarrays.scala:380 |
vecxt.NDArrayFloatOps$.compareScalarGeneral |
@HotPath |
165 | 51% of 325 | yes | ndarrayFloatOps.scala:94 |
vecxt.NDArrayFloatOps$.binaryOpInPlaceGeneral |
@HotPath |
162 | 50% of 325 | yes | ndarrayFloatOps.scala:119 |
vecxt.NDArrayDoubleOps$.compareScalarGeneral |
@HotPath |
157 | 48% of 325 | yes | ndarrayDoubleOps.scala:102 |
vecxt.NDArrayIntOps$.compareScalarGeneral |
@HotPath |
156 | 48% of 325 | yes | ndarrayIntOps.scala:94 |
vecxt.NDArrayIntOps$.binaryOpInPlaceGeneral |
@HotPath |
153 | 47% of 325 | yes | ndarrayIntOps.scala:119 |
vecxt.NDArrayDoubleOps$.binaryOpInPlaceGeneral |
@HotPath |
153 | 47% of 325 | yes | ndarrayDoubleOps.scala:131 |
vecxt.NDArrayIntOps$.unaryOpGeneral |
@HotPath |
152 | 47% of 325 | yes | ndarrayIntOps.scala:42 |
vecxt.NDArrayDoubleOps$.unaryOpGeneral |
@HotPath |
152 | 47% of 325 | yes | ndarrayDoubleOps.scala:48 |
vecxt.doublearrays$.fillLinspace |
@AllocFree @HotPath |
133 | 41% of 325 | yes | doublearrays.scala:34 |
vecxt.intarrays$.dot |
@AllocFree @HotPath |
129 | 40% of 325 | yes | intarrays.scala:319 |
vecxt.intarrays$.increments |
@HotPath |
121 | 37% of 325 | yes | intarrays.scala:188 |
vecxt.ndarray$.mkNDArray |
@Thin |
13 | 37% of 35 | no | ndarray.scala:178 |
vecxt.doublearrays$.increments |
@HotPath |
110 | 34% of 325 | yes | doublearrays.scala:360 |
vecxt.floatarrays$.increments |
@HotPath |
97 | 30% of 325 | yes | floatarrays.scala:615 |
vecxt.doublearrays$.$times$times$bang |
@AllocFree @HotPath |
95 | 29% of 325 | yes | doublearrays.scala:335 |
vecxt.doublearrays$.$minus$eq |
@AllocFree @HotPath |
92 | 28% of 325 | yes | doublearrays.scala:1009 |
vecxt.doublearrays$.$plus$eq |
@AllocFree @HotPath |
90 | 28% of 325 | yes | doublearrays.scala:944 |
vecxt.doublearrays$.$times$eq |
@AllocFree @HotPath |
88 | 27% of 325 | yes | doublearrays.scala:1081 |
vecxt.doublearrays$.productSIMD |
@AllocFree @HotPath |
86 | 26% of 325 | yes | doublearrays.scala:646 |
vecxt.floatarrays$.$times$times$bang |
@AllocFree @HotPath |
85 | 26% of 325 | yes | floatarrays.scala:277 |
vecxt.doublearrays$.sumSIMD |
@AllocFree @HotPath |
85 | 26% of 325 | yes | doublearrays.scala:623 |
vecxt.doublearrays$.fma$bang |
@AllocFree @HotPath |
85 | 26% of 325 | yes | doublearrays.scala:984 |
vecxt.intarrays$.$plus$eq |
@AllocFree @HotPath |
84 | 26% of 325 | yes | intarrays.scala:496 |
vecxt.intarrays$.$minus$eq |
@AllocFree @HotPath |
84 | 26% of 325 | yes | intarrays.scala:469 |
vecxt.floatarrays$.$times$eq |
@AllocFree @HotPath |
84 | 26% of 325 | yes | floatarrays.scala:837 |
vecxt.floatarrays$.$plus$eq |
@AllocFree @HotPath |
84 | 26% of 325 | yes | floatarrays.scala:735 |
vecxt.floatarrays$.$minus$eq |
@AllocFree @HotPath |
84 | 26% of 325 | yes | floatarrays.scala:775 |
vecxt.doublearrays.meanAndVariance |
@Thin |
9 | 26% of 35 | no | doublearrays.scala |
vecxt.doublearrays$.meanAndVariance |
@Thin |
9 | 26% of 35 | no | doublearrays.scala:501 |
vecxt.intarrays$.minSIMD |
@AllocFree @HotPath |
82 | 25% of 325 | yes | intarrays.scala:516 |
vecxt.intarrays$.maxSIMD |
@AllocFree @HotPath |
82 | 25% of 325 | yes | intarrays.scala:536 |
vecxt.floatarrays$.productSIMD |
@AllocFree @HotPath |
82 | 25% of 325 | yes | floatarrays.scala:500 |
vecxt.intarrays$.sumSIMD |
@AllocFree @HotPath |
81 | 25% of 325 | yes | intarrays.scala:233 |
vecxt.floatarrays$.sumSIMD |
@AllocFree @HotPath |
81 | 25% of 325 | yes | floatarrays.scala:479 |
vecxt.floatarrays$.fma$bang |
@AllocFree @HotPath |
79 | 24% of 325 | yes | floatarrays.scala:303 |
vecxt.floatarrays$.$times$eq |
@AllocFree @HotPath |
78 | 24% of 325 | yes | floatarrays.scala:889 |
vecxt.ndarray.shapeArray |
@Thin |
8 | 23% of 35 | no | ndarray.scala |
vecxt.intarrays$.$minus$eq |
@AllocFree @HotPath |
67 | 21% of 325 | yes | intarrays.scala:351 |
vecxt.floatarrays$.$plus$eq |
@AllocFree @HotPath |
24 | 7% of 325 | no | floatarrays.scala:694 |
Method sizes
| band | methods |
|---|---|
| <= 6 (trivial, always inlined) | 1409 |
| 7-35 (inlinable cold) | 2944 |
| 36-325 (inlinable when hot) | 595 |
| 326-8000 (not inlined) | 112 |
| > 8000 (NEVER JIT COMPILED) | 0 |
| bytes | method | module | at |
|---|---|---|---|
| 6996 | CheatsheetTest$.matrixRangeSlicing |
experiments | cheatsheet.scala:118 |
| 4480 | CheatsheetTest$.matrixReverseSlicing |
experiments | cheatsheet.scala:125 |
| 4329 | CheatsheetTest$.ndArrayBoolean |
experiments | cheatsheet.scala:430 |
| 4126 | CheatsheetTest$.ndArrayInt |
experiments | cheatsheet.scala:416 |
| 3549 | CheatsheetTest$.ndArrayFloat |
experiments | cheatsheet.scala:395 |
| 3283 | CheatsheetTest$.matrixOps |
experiments | cheatsheet.scala:167 |
| 3256 | CheatsheetTest$.matrixCreation |
experiments | cheatsheet.scala:72 |
| 3167 | CheatsheetTest$.ndArrayFloatReductions |
experiments | cheatsheet.scala:405 |
| 2500 | vecxt_re.Tower.show |
vecxt_re | Tower.scala:63 |
| 2042 | vecxt.JvmDoubleMatrix$.$plus$eq |
vecxt | doublematrix.scala:315 |
| 2028 | vecxt.JvmFloatMatrix$.floatmatrixAddScalarInPlace |
vecxt | floatmatrix.scala:442 |
| 2028 | vecxt.JvmFloatMatrix$.floatmatrixSubScalarInPlace |
vecxt | floatmatrix.scala:514 |
| 2023 | CheatsheetTest$.arrayMath |
experiments | cheatsheet.scala:253 |
| 1921 | CheatsheetTest$.indexingAndSlicing |
experiments | cheatsheet.scala:98 |
| 1594 | vecxt.ndarrayOps$.apply |
vecxt | ndarrayOps.scala:434 |
| 1556 | vecxt.DoubleMatrix$.$minus$colon$minus |
vecxt | doublematrix.scala:232 |
| 1554 | vecxt.DoubleMatrix$.$plus$colon$plus |
vecxt | doublematrix.scala:143 |
| 1504 | vecxt.JvmFloatMatrix$.floatmatrixSubVector |
vecxt | floatmatrix.scala:336 |
| 1478 | CheatsheetTest$.arrayManipulation |
experiments | cheatsheet.scala:305 |
| 1403 | vecxt.Svd$.pinv |
vecxt | svd.scala:42 |
| 1346 | vecxt.JvmDoubleMatrix$.$plus$eq |
vecxt | doublematrix.scala:227 |
| 1322 | vecxt.JvmFloatMatrix$.floatmatrixAddVectorInPlace |
vecxt | floatmatrix.scala:248 |
| 1322 | vecxt.JvmFloatMatrix$.floatmatrixSubVectorInPlace |
vecxt | floatmatrix.scala:360 |
| 1198 | CheatsheetTest$.matrixFloat |
experiments | cheatsheet.scala:445 |
| 1102 | vecxt.DoubleMatrix$.hadamard |
vecxt | doublematrix.scala:171 |
Proposed baseline
{
"jdkMajor": 25,
"c9": { "totalBytes": 51103, "distinctOps": 178 },
"annotated": {
"vecxt.NDArrayDoubleOps$.binaryOpGeneral(Lvecxt/ndarray$NDArray;Lvecxt/ndarray$NDArray;Lscala/Function2;)Lvecxt/ndarray$NDArray;": 186,
"vecxt.NDArrayDoubleOps$.binaryOpInPlaceGeneral(Lvecxt/ndarray$NDArray;Lvecxt/ndarray$NDArray;Lscala/Function2;)V": 153,
"vecxt.NDArrayDoubleOps$.compareGeneral(Lvecxt/ndarray$NDArray;Lvecxt/ndarray$NDArray;Lscala/Function2;)Lvecxt/ndarray$NDArray;": 186,
"vecxt.NDArrayDoubleOps$.compareScalarGeneral(Lvecxt/ndarray$NDArray;DLscala/Function2;)Lvecxt/ndarray$NDArray;": 157,
"vecxt.NDArrayDoubleOps$.unaryOpGeneral(Lvecxt/ndarray$NDArray;Lscala/Function1;)Lvecxt/ndarray$NDArray;": 152,
"vecxt.NDArrayFloatOps$.binaryOpGeneral(Lvecxt/ndarray$NDArray;Lvecxt/ndarray$NDArray;Lscala/Function2;)Lvecxt/ndarray$NDArray;": 195,
"vecxt.NDArrayFloatOps$.binaryOpInPlaceGeneral(Lvecxt/ndarray$NDArray;Lvecxt/ndarray$NDArray;Lscala/Function2;)V": 162,
"vecxt.NDArrayFloatOps$.compareGeneral(Lvecxt/ndarray$NDArray;Lvecxt/ndarray$NDArray;Lscala/Function2;)Lvecxt/ndarray$NDArray;": 195,
"vecxt.NDArrayFloatOps$.compareScalarGeneral(Lvecxt/ndarray$NDArray;FLscala/Function2;)Lvecxt/ndarray$NDArray;": 165,
"vecxt.NDArrayIntOps$.binaryOpGeneral(Lvecxt/ndarray$NDArray;Lvecxt/ndarray$NDArray;Lscala/Function2;)Lvecxt/ndarray$NDArray;": 186,
"vecxt.NDArrayIntOps$.binaryOpInPlaceGeneral(Lvecxt/ndarray$NDArray;Lvecxt/ndarray$NDArray;Lscala/Function2;)V": 153,
"vecxt.NDArrayIntOps$.compareGeneral(Lvecxt/ndarray$NDArray;Lvecxt/ndarray$NDArray;Lscala/Function2;)Lvecxt/ndarray$NDArray;": 186,
"vecxt.NDArrayIntOps$.compareScalarGeneral(Lvecxt/ndarray$NDArray;ILscala/Function2;)Lvecxt/ndarray$NDArray;": 156,
"vecxt.NDArrayIntOps$.unaryOpGeneral(Lvecxt/ndarray$NDArray;Lscala/Function1;)Lvecxt/ndarray$NDArray;": 152,
"vecxt.doublearrays$.$minus$eq([DD)V": 92,
"vecxt.doublearrays$.$plus$eq([DD)V": 90,
"vecxt.doublearrays$.$times$eq([D[D)V": 88,
"vecxt.doublearrays$.$times$times$bang([DD)V": 95,
"vecxt.doublearrays$.clamp$bang([DDD)V": 180,
"vecxt.doublearrays$.fillLinspace([DDD)V": 133,
"vecxt.doublearrays$.fma$bang([DDD)V": 85,
"vecxt.doublearrays$.increments([D)[D": 110,
"vecxt.doublearrays$.meanAndVariance([D)Lscala/Tuple2;": 9,
"vecxt.doublearrays$.productSIMD([D)D": 86,
"vecxt.doublearrays$.sumSIMD([D)D": 85,
"vecxt.doublearrays.meanAndVariance([DLvecxt/VarianceMode;)Lscala/Tuple2;": 9,
"vecxt.floatarrays$.$minus$eq([FF)V": 84,
"vecxt.floatarrays$.$plus$eq([FF)V": 84,
"vecxt.floatarrays$.$plus$eq([F[F)V": 24,
"vecxt.floatarrays$.$times$eq([FF)V": 78,
"vecxt.floatarrays$.$times$eq([F[F)V": 84,
"vecxt.floatarrays$.$times$times$bang([FF)V": 85,
"vecxt.floatarrays$.clamp$bang([FFF)V": 172,
"vecxt.floatarrays$.fma$bang([FFF)V": 79,
"vecxt.floatarrays$.increments([F)[F": 97,
"vecxt.floatarrays$.productSIMD([F)F": 82,
"vecxt.floatarrays$.sumSIMD([F)F": 81,
"vecxt.intarrays$.$minus$eq([II)V": 67,
"vecxt.intarrays$.$minus$eq([I[I)V": 84,
"vecxt.intarrays$.$plus$eq([I[I)V": 84,
"vecxt.intarrays$.dot([I[I)I": 129,
"vecxt.intarrays$.increments([I)[I": 121,
"vecxt.intarrays$.maxSIMD([I)I": 82,
"vecxt.intarrays$.minSIMD([I)I": 82,
"vecxt.intarrays$.sumSIMD([I)I": 81,
"vecxt.ndarray$.mkNDArray(Ljava/lang/Object;[I[II)Lvecxt/ndarray$NDArray;": 13,
"vecxt.ndarray.shapeArray(Lvecxt/ndarray$NDArray;)[I": 8
}
}
|
@copilot please look at the bytecode test failure? |
…ods) Co-authored-by: Quafadas <24899792+Quafadas@users.noreply.github.com>
The Fixed in the latest commit by removing |
|
@copilot This constructor is unvalidated matrix.scala:212 - can you fix it pelase? |
…ed Thin import Co-authored-by: Quafadas <24899792+Quafadas@users.noreply.github.com>
Fixed in the latest commit. |
Matrix[A]carried five layout primitives and four derivedvals as flat class members, with ~50 hand-copied index-arithmetic expressions and ~13 verbatim 5-positional-Intconstructions scattered across the codebase — a silent transpose bug waiting to happen.Phase 1 —
Layoutextraction (vecxt/src/matrix.scala)final class Layoutwith 7 fields (rows,cols,rowStride,colStride,offset,dataLength,kind) and derivedvals (numel,isDenseColMajor,isDenseRowMajor,hasSimpleContiguousMemoryLayout).final class(notcase class, notsealed trait) keeps getters at 5 bytes — underMaxTrivialSize(6).@Thin inline def linearIndex(row, col)— replaces all hand-writtenoffset + i*rowStride + j*colStride. Measured < 35 bytes (MaxInlineSize).Layout.transpose,Layout.withDataLength,Layout.sameElementOrderAsderived operations.hasSimpleContiguousMemoryLayoutnow baked intoLayoutviadataLength == numel— removes the last genericArray[A].sizeread fromMatrix's construction path (C6a fix).Matrix[A]rewritten to hold a singleval layout: Layoutwithinlineforwarders for all existing fields — zero source-compatibility breaks.Matrix.apply(raw, layout)checked factory (throwsIllegalArgumentExceptiononraw.size != layout.dataLength);Matrix.mkMatrixuncheckedprivate[vecxt]path for internal callers.object LayoutwithStrided: Byte = 0;kindfield reserved for Phase 3.Matrix#layout: Stringrenamed tolayoutString(8 sites updated).Phase 2 — adopt
Layoutat call sitesMatrix(newArr, m.rows, m.cols, m.rowStride, m.colStride, m.offset)→Matrix(newArr, m.layout)) acrossdoublematrix.scalaandfloatmatrix.scala. Sites allocatingArray.ofDim[Double](m.numel)left unchanged — those arrays may differ in size fromm.raw.m.offset + i*m.rowStride + j*m.colStride→m.layout.linearIndex(i, j)) acrossdoublematrix.scala,floatmatrix.scala,intmatrix.scala,doublematrix_native.scala, andMatrixInstance.scala. Loop-hoisted expressions preserved.matrixutil.transpose→Matrix.mkMatrix(m.raw, m.layout.transpose).sameDenseElementWiseMemoryLayoutCheckdelegates toa.layout.sameElementOrderAs(b.layout)(exact operator precedence preserved).New test coverage
vecxt/test/src/layout.test.scala— derived flag correctness,linearIndex(strided/offset/transposed/broadcast),transposeround-trip property,sameElementOrderAs,equals/hashCodeconsistency,Matrix.apply(raw, layout)invariant enforcement.Matrix[A]'s layout fields into aLayoutvalue #108