Repository navigation
Speed up the hot paths, no behaviour change - #617
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Advanced Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
commit: |
Tree-shaking report✅ No size regression. 16 grew, 8 shrank out of 198 exports.
What changed (24)
Show the other 4
All exports (198)
How this is measuredEvery export is imported alone into an esbuild consumer bundle (minified, tree-shaken) built from the head and from the base of this pull request; the sizes are the resulting bundles, gzip is their gzipped size. 🔴 marks a regression: a pre-existing export that grew more than 20% and more than 256 B, or the bundle importing every pre-existing export growing more than 5%. 🟡 is growth under the threshold, 🟢 a decrease, ⚪ no change, 🆕 an export that does not exist on the base (never a regression), 🗑️ an export that was removed. An intentional increase is accepted with the |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## claude/bundle-shrink #617 +/- ##
======================================================
Coverage 100.00% 100.00%
======================================================
Files 255 261 +6
Lines 2558 2640 +82
Branches 745 752 +7
======================================================
+ Hits 2558 2640 +82
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
1e09c47 to
e97d953
Compare
e97d953 to
c1b3309
Compare
c1b3309 to
2fc772c
Compare
|
Deployment failed for project brazilian-utils with the following error: Learn More: https://vercel.com/hyan-mandians-projects?upgradeToPro=build-rate-limit |
2fc772c to
b69936c
Compare
… dates isHoliday, isBusinessDay, addBusinessDays, subBusinessDays and differenceInBusinessDays compared against freshly built Date objects on every call. They now read the memoized year, month and day directly, and the business day walks validate their options once. ops/sec (Mops/s, before -> after): isHoliday 0.94 -> 8.6, isBusinessDay 1.23 -> 17.4.
…m per call Municipality name, state name, bank code and bank ISPB lookups scanned (and re-normalized) their tables on every call; getCid10 walked the 2,046 categories per lookup; getMunicipalitiesByAreaCode filtered every municipality of the states. Each now reads a cache built on first use. getBanks copies with a literal instead of Object.assign. ops/sec (before -> after): getCodeByMunicipalityName 9.4K -> 4.9M, getCid10 51K -> 8.0M, getStateCodeByName 0.23M -> 6.8M, getMunicipalitiesByAreaCode 72K -> 2.6M, getBankByCode 3.8M -> 18.9M, getBankByIspb 2.6M -> 17.9M, getMunicipalities() 289 -> 31K.
The default lower and upper case sets were rebuilt on every call and are now built once, and case folding uses toUpperCase/toLowerCase, which give the same result as the pt-BR locale (only tr, az and lt tailor case mapping). ops/sec (before -> after): capitalize 0.28M -> 0.92M.
crc16Ccitt looped over the bits of every byte and built a TextEncoder per call; it now uses a 256 entry table built on first use and reads ASCII text straight from the string, keeping the UTF-8 path for anything else. parseTlv reads the two digit id and length with char codes instead of slicing and testing two regexes per field. ops/sec (before -> after): isValidPixPayload 1.18M -> 1.9M, generatePixPayload 0.55M -> 0.85M, getPixPayloadInfo 0.78M -> 0.97M.
getCpfInfo filtered the 27 state codes on every call; the states of each fiscal region are now computed once and copied. ops/sec (before -> after): getCpfInfo 4.4M -> 6.7M.
eachLocalDay built two Date objects per day walked; it now updates one Date with setTime and builds only the local noon candidate it yields. ops/sec (before -> after): addBusinessDays +250 (on top of the memo change) 16K -> 18K, differenceInBusinessDays 7.1K -> 8.2K.
getCities() copies a cached, sorted list on every call. Since the names are unpacked from the packed tables, the list built with flatMap and an in place sort was copied at 0.45M ops/s; the same list built with toSorted is copied at 1.9M ops/s, the speed it had before the tables were packed.
…f the public modules readBusinessDayRules, isBusinessDayUnderRules, BusinessDayRules, readMemoizedHolidays and MemoizedHoliday were exported from src/is-business-day and src/get-holidays, which are build entries, so they leaked into the declarations of those two subpaths. Each now lives in its own _internals folder (the holiday memo with computeHolidays and its cache), with a test of its own. The declarations of the subpaths are back to the public API.
The text said no Date is ever mutated, which stopped being literally true when the walk began reusing one private scratch Date. It now says that no Date a caller sees is mutated and that the walk reads the scratch Date through its UTC getters. The paragraph is rewrapped to the 100 column width of the rest of the block.
…ric already does sanitizeToAlphanumeric already upper cases its result, so the toUpperCase after it never changed the key. The behavior is identical.
… with toSorted, like getCities getCities and getMunicipalities built their cached sorted list with different calls. Both use toSorted now; the ops per second of getMunicipalities() are unchanged (74k before and after).
… internal The lazy table of category offsets sat in getCid10.ts next to the lazy table of subcategories it is built from. It now has its own _internals folder and test, and getCid10 only reads it.
…lone Reordering or splicing the states of one result must not reach the next call nor another region, and two results never share an array.
The 100% mutation threshold failed on the fast paths this branch adds. Most are caches and shortcuts whose slow path returns the very same value, so their mutants are equivalent and carry a disable directive with the reason: the lookup and word set caches and the UTF-8 pass and typed array bound of crc16Ccitt. New tests kill the rest: crc16Ccitt checks the UTF-8 bytes of U+0080, parseTlv rejects an id whose digit is one code off the range, and capitalize reads the default upper case words on its first call. No behavior changes.
…llable mutants The lookup caches checked `if (!index)` behind a Stryker directive, which also silenced the mutant that turns the check into `if (false)`, a crash that the tests do kill. The caches of getCodeByMunicipalityName, getCpfInfo and getMunicipalitiesByAreaCode now read through a new readCached internal, `cache.get(key) ?? store(...)`, which has a test of its own (each key built once, the same value handed back). getStateCodeByName builds its index with `??=` and a builder, and findBank keeps one lazily built index per key. The five directives are gone and Stryker still scores 100% on each file. crc16Ccitt builds its table with Uint16Array.from, which drops the loop bound directive, and leaves the ASCII pass through one early return, which leaves one directive where there were three, all on the check whose `true` variant only sends an ASCII string down the UTF-8 pass. The capitalize test for the default upper case words no longer carries the order it depends on in its title. Only the first test that reaches the lazily built default set can see an emptied list, so the directive on that array now says so. Stryker scores 100% on capitalize with the test in another position and under shuffled test order. No behavior changes.
The lookup caches and the tables they read add up to 0.3 KB to the dataset utils, so the size table is measured again with node scripts/tree-shaking.ts on this branch.
b69936c to
2b562e0
Compare
Third PR of the stack (base: the bundle PR). Runtime performance of the hot paths with zero behaviour change: a differential check compared every export of this branch with #615 on 12.9M calls (valid, masked, hostile strings, numbers,
null, null-prototype objects, a throwingtoString, holidays across dates and states) and found 0 differences.Biggest wins (ops/s, #615 → this PR, Node, same machine, best of 5)
getCodeByMunicipalityNamegetCid10getMunicipalities()getMunicipalitiesByAreaCodegetMunicipalityByCodegetStateCodeByNameisBusinessDayisHolidaygetBankByCodecapitalizeisValidPixPayload,getPixPayloadInfogetCpfInfoWhat changed
isHoliday/isBusinessDayread the memoized days instead of rebuildingDateobjects per call; the business-day walk reuses oneDate(these exports also get 218 to 258 B smaller)._internals/read-cached.capitalize: default word sets cached (the linear word scan itself is in Validation round: fix every audited edge case, docs and convention drift #615).getCpfInfocaches the states of each fiscal region;getCities()builds its cached list withtoSorted.Size
Speed never bought bytes on shared helpers: fast paths in the sanitizers,
toStringSafe,removeAccentsand the mask helper were measured (up to 3x on functions already above 5M ops/s, for +40 to +290 B on 130+ exports) and left out. 185 of the 198 exports are within 15 B of #616 (isValidCpf500 B andisValidCnpj796 B unchanged). The 13 that grow are the ones that gain algorithmically:isValidPixPayload/getPixPayloadInfo+323 B,getMunicipality/getCodeByMunicipalityName+257 B,generatePixPayload+250 B,getMunicipalitiesByAreaCode+134 B,getCpfInfo+123 B,getBankByCode/getBankByIspb+114 B,getMunicipalities+82 B,getStateCodeByName+60 B,getCid10+48 B,getBanks+28 B. Whole package: 1,420,542 B / 300,083 gzip (#616: 1,419,421 / 299,486;main: 1,889,707 / 335,145).Gates
vp check, tests with 100% coverage (also underTZ=America/Sao_PauloandTZ=UTC), build, jscpd, knip,check:api, commitlint, Stryker 100% on the changed files.