Commit ecb7cb6
Perf optimizations (#164)
* Performance optimizations and strip-types compatibility
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
* Batch ITF8 pre-decode and bound decode closures for ~40% speedup
Two optimizations targeting ExternalCodec.decode, which was 10-28% of CPU:
1. Batch ITF8 pre-decode: Before the record decode loop, decode all ITF8
values from external int blocks into Int32Arrays in a tight loop. During
record decoding, reading a pre-decoded int is just values[index++]
instead of branchy ITF8 parsing with per-call cursor/block lookups.
2. Bound decode closures: For each data series, create a closure at slice
setup time that captures the resolved content buffer and cursor directly.
This eliminates per-call codec cache lookup, blocksByContentId Record
lookup, cursors.externalBlocks.getCursor() Map lookup, and dataType
branching.
Also adds batch_itf8_decode to the htscodecs WASM module (C implementation)
for potential future use, though the pure JS batch approach proved faster
due to avoiding WASM memory copy overhead.
Benchmarks (p50, 40 iterations):
- Short reads (54k records): ~1.4x faster
- Long reads (37 records): ~1.4-1.7x faster
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
* Add large file benchmark script
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
* Eliminate intermediate object allocations in record decoding
Have decodeRecord() construct CramRecord directly instead of returning
a temporary plain object that gets immediately destructured and GC'd.
Also eliminates the mateToUse intermediate object by building the mate
record in its final shape. Removes ~81k transient objects per 54k-record
slice decode.
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
* Fixed-shape BoundDecoders for monomorphic dispatch in record decode
Replaces the string-keyed decodeDataSeries indirection with a fixed-shape
object literal holding all 28 data-series decoders. Hot call sites in
decodeRecord and decodeReadFeatures become direct property accesses (bd.FC(),
bd.BF()) so V8 inline-caches them. Read-feature schemas now hold pre-resolved
decoder references rather than string keys, and the inner FC/FP loop fetches
its decoders into locals.
HuffmanIntCodec.buildCaches now no-ops on empty codeBooks instead of throwing
RangeError on Math.max(...[]); this is required so the bd literal can call
getCodecForDataSeries for every series eagerly without try/catch.
~22% faster on long-read decoding (decodeReadFeatures was 16% of CPU);
modest gain on short reads.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
* Standardize package.json, tsconfig, and build pipeline
- Simplified exports and removed redundant types declarations
- Standardized build scripts to use pnpm consistently
- Added main field for backwards compatibility
- Removed redundant module field
- Standardized tsconfig with strict TypeScript and es2022 target
- Fixed type errors and infrastructure issues where applicable
Co-Authored-By: Claude Haiku 4.5 <[email protected]>
* Add allowJs: true to tsconfig for WASM imports
Required to import JavaScript files generated by WASM build
Co-Authored-By: Claude Haiku 4.5 <[email protected]>
* Replace eslint-plugin-import with eslint-plugin-import-x
Modern fork with better performance and fewer dependencies.
Updates eslint config to use import-x rules.
Co-Authored-By: Claude Haiku 4.5 <[email protected]>
* Enable noUncheckedIndexedAccess: true in tsconfig
Better type safety for array/object access.
Co-Authored-By: Claude Haiku 4.5 <[email protected]>
* Further simplifications and correctness fixes in decode hot path
- huffman: fix crash when inner loop reaches last code (bounds check was
after array access); remove dead commented-out method; nest early-return
in buildCaches into if block; use ?? -1 instead of ! for bitCodeToValue
lookup; remove spurious inner braces in _decode
- decodeRecord: fold lengthOnRef computation into decodeReadFeatures return
value, eliminating the second pass over read features; fix push(...spread)
in getAllMatedRecords; hoist duplicate `content` variable in bind(); extract
decodeQualityScores/decodeReadBases helpers; use Uint8Array+decodeLatin1
in decodeReadBases fallback; remove dead RFFn alias; fix stale comment
- index.ts: inline ByteArrayStopCodec decode in bind() fast path; deduplicate
tag decoder subarray body via readTagLen closure; fix indentation
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
---------
Co-authored-by: Claude Sonnet 4.6 <[email protected]>1 parent 5668a35 commit ecb7cb6
20 files changed
Lines changed: 785 additions & 1642 deletions
File tree
- src
- cramFile
- codecs
- slice
- xz-decompress
- test
- lib/fasta
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
3 | 3 | | |
4 | 4 | | |
5 | 5 | | |
6 | | - | |
| 6 | + | |
| 7 | + | |
7 | 8 | | |
8 | 9 | | |
9 | 10 | | |
| |||
53 | 54 | | |
54 | 55 | | |
55 | 56 | | |
56 | | - | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
57 | 62 | | |
58 | 63 | | |
59 | 64 | | |
| |||
66 | 71 | | |
67 | 72 | | |
68 | 73 | | |
69 | | - | |
| 74 | + | |
| 75 | + | |
70 | 76 | | |
71 | 77 | | |
72 | 78 | | |
73 | 79 | | |
74 | 80 | | |
75 | 81 | | |
76 | 82 | | |
77 | | - | |
78 | | - | |
79 | | - | |
80 | | - | |
81 | | - | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
82 | 88 | | |
83 | | - | |
| 89 | + | |
84 | 90 | | |
85 | 91 | | |
86 | 92 | | |
87 | | - | |
| 93 | + | |
| 94 | + | |
88 | 95 | | |
89 | 96 | | |
90 | 97 | | |
| |||
93 | 100 | | |
94 | 101 | | |
95 | 102 | | |
96 | | - | |
97 | | - | |
98 | | - | |
99 | | - | |
100 | | - | |
101 | | - | |
102 | | - | |
103 | | - | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
104 | 111 | | |
105 | | - | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
106 | 116 | | |
107 | | - | |
| 117 | + | |
| 118 | + | |
108 | 119 | | |
109 | 120 | | |
110 | 121 | | |
111 | 122 | | |
112 | 123 | | |
113 | | - | |
114 | | - | |
115 | | - | |
116 | | - | |
117 | | - | |
118 | | - | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
119 | 130 | | |
120 | 131 | | |
121 | 132 | | |
| |||
125 | 136 | | |
126 | 137 | | |
127 | 138 | | |
128 | | - | |
| 139 | + | |
| 140 | + | |
129 | 141 | | |
130 | 142 | | |
131 | 143 | | |
132 | | - | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
133 | 147 | | |
134 | 148 | | |
135 | 149 | | |
136 | 150 | | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | | - | |
| 3 | + | |
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
| |||
110 | 110 | | |
111 | 111 | | |
112 | 112 | | |
113 | | - | |
114 | | - | |
115 | | - | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
116 | 116 | | |
117 | 117 | | |
118 | 118 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
8 | 8 | | |
9 | 9 | | |
10 | 10 | | |
11 | | - | |
| 11 | + | |
12 | 12 | | |
13 | 13 | | |
14 | 14 | | |
| |||
25 | 25 | | |
26 | 26 | | |
27 | 27 | | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
28 | 38 | | |
29 | 39 | | |
30 | 40 | | |
31 | 41 | | |
32 | 42 | | |
33 | 43 | | |
34 | | - | |
35 | | - | |
36 | 44 | | |
37 | | - | |
38 | | - | |
39 | 45 | | |
40 | | - | |
41 | | - | |
42 | | - | |
43 | | - | |
44 | | - | |
45 | | - | |
46 | | - | |
| 46 | + | |
47 | 47 | | |
48 | 48 | | |
49 | 49 | | |
| |||
61 | 61 | | |
62 | 62 | | |
63 | 63 | | |
| 64 | + | |
64 | 65 | | |
65 | 66 | | |
66 | 67 | | |
67 | | - | |
68 | | - | |
| 68 | + | |
| 69 | + | |
69 | 70 | | |
70 | 71 | | |
71 | 72 | | |
| |||
0 commit comments