Repository navigation
perf(reader): the definitions pass, the wall records read and the catalog's document build only the fields they read; the preamble does less (#1335) - #1483
Merged
Conversation
7 of 8 tasks
… hex without a string per byte and reads the catalog's document without building what it ignores (#1335)
This was referenced Nov 5, 2026
Closed
…reader, the probe imports them, and the index leaves are named fields (#1335)
DrunkOnJava
marked this pull request as ready for review
November 5, 2026 23:44
This was referenced Nov 5, 2026
…ers in code spans, not links (#1335)
DrunkOnJava
enabled auto-merge (squash)
November 6, 2026 00:39
12 tasks done
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.
Summary
One mechanism, three cuts, for #1335: a native pass that reads a few fields of a graph now reads and checks the whole graph exactly as before and builds only the fields it reads (
native_parameters::decode_graph_projected,Options::graph_fields). It is used by the parameter definitions pass (itsFamilyandFamilySymbolrecords), the read of the wall records (SWallandArcWall) and the extensible storage catalog's document; the preamble's element index and checksum hex also do less work. Folded from #1483, #1485 and #1488; the phase read (#1487) is not changed and is recorded below as a measured floor.Measured at 4140b96 (
mainmerged at df96767). The pushed head, 1811752, mergesmainat 8060ecc cleanly: no conflict in the files this changes, and #1470's additions tonative_walls.rsare innative_walls_and_top_joins_into, which reads in full, not inWallReads::read.rst_basic91 to 94 down to 79 to 80, Core Interior 324 to 310, RE1 Plumbing 73 to 67, Snowdon Towers plumbing 513 to 462. The four rises: 2026racbasicsampleproject225 to 233 MiB (1.03), and RE1 Electrical, 2015rac_advancedand the BIM4LCA office at building permit by 1 MiB each. Why the 8 MiB is not measured.main(the same unfiltered run, paired on one runner per shard, median of three): 0.867 on the 2015 samples, 0.878 on 2016 to 2018, 0.862 on 2019 to 2022, 0.903 on 2023, 0.968 on 2024 to 2027, 0.972 on the reference models and offices; 0.947 over the 58 samples (mean of the per-file ratios 0.914, median 0.901) and 0.953 over all 68 files. The table is below.ifc.sha256equal on all 111 exports of the unfiltered run,diagnostics.jsonequal on the 93 that upload one, and the 18 held-out models'counts.txtandifc.sha256equal; of the 2,251 result files common to the two sides, 2,233 are byte-identical and the 18 that differ are the runs' owncommit.txt,runner-*.txt,vmstat-*.logandscore-cache-*.log. The semantic check says "no semantic difference in 34 models".Motivation
#1335: the definitions pass is
FamilySymbol207 ms decode + 110 ms freeing,Family28 + 8, of its 403 ms on 2015rac_advanced(profile run 42111526405, scratch head cc7f9c1, one loaded runner; shares only). Over the 33 samples of 2015 to 2023 that profile has the passes at: definitions 9,843 of 63,116 ms (15.6%), wall records 2,227 (3.5%), phase read 1,946 (3.1%), native preamble 2,499 (4.0%), record index 1,220 (1.9%). On a quiet runner the cuts are smaller than the shares: 8.1%, 2.4% and 1.2%."The shared preamble before the native passes, about 590 ms" is not the native preamble. It is everything an export does before its first native pass (the partition strings, the arc wall scan, the 2023 record scan, the levels), which 0.11.0 does as well: profiled on both (run 42111526405 on an EPYC 9V74 and run 42112096504, scratch head 03aa8f6, on an EPYC 7763: different runners, so shares only) those steps are 65.7% of the export of 2015
rac_advancedat 0.11.0 and 35.5% onmain, and among the steps both versions run each takes about the same share on both (the partition strings 35% at 0.11.0 and 29% onmain, the 2023 record scan 17% and 18%, writing the file 15% and 18%). They are a floor shared with 0.11.0. What the native passes added isread_preamble: 68 ms on 2015rac_advanced: schema parse 11 ms, element index parse 18 ms, storage increments 4 ms, extensible storage catalog 28 ms.The issue's concern about the definitions pass: a root-only read could ingest a record the full decode refuses today. This does not read less: every byte is read and every check made, in the same order and with the same errors, and only the values of the fields not kept are not built. A record the full decode refuses is refused with the same message.
Changes
src/research/native_parameters.rs: the cursor'spointer,entity_pointer,classandfieldtakeconst BUILD: bool. With it unset they read, check and note pointers exactly as with it set (one body, so the checks cannot differ) and returnNullin place of the value.decode_graph_projected(.., keep)decodes a graph whose objects keep only the fields named inkeep. A repeated field key is found in the built object where there is one, and by position (ClassPlan::duplicate, found only when projecting) where the field is not built. The full decode's path is the same statements as before.src/research/native_document.rs:Options::graph_fields, a function from a record's root class to the fields to keep; a projected record has nosaved_metadata. The definitions pass sets its own, whatever the caller's.pub constbeside the one function that reads the projected graph, so that a field the reader comes to read is added where it is read:native_parameter_definitions::{FAMILY_BINDING_FIELDS, SYMBOL_BINDING_FIELDS}andRegistry::ingest_binding_context;native_walls::WALL_READ_FIELDS(m_id,m_WallAttributesId,m_baseOrTop) andwall_facts, whichWallReads::readcalls;native_es_catalog::CATALOG_FIELDSandfrom_graph, whichdecodecalls. Doc lines at those readers and atwall_type_idandis_top_joinedsay so, anddocs/maintaining.mdhas a section on it.src/research/native_index.rs:parsesearched the element table's layout for the leaf of each of the 7 fields it takes, at each of the file's rows (17,101 onrac_advanced), with a vector of candidates each time. The leaf a prefix names is now found at the first row that asks for it and kept in a named field ofIndexLeaves, so a lookup that fails fails at the same point with the same message and an eighth lookup is a new field, not an index.src/research/schema_registry.rs,src/research/native_es_catalog.rs,src/research/native_document.rs: the checksums of the schema (one per class) and of the catalog's stream are formatted withlower_hex, the same lowercase digits without a string for each byte.probes/examples/probe_1335_projected_graphs.rs: imports the constants, decodes every record both ways under each list and under two that stress the mechanism (the identity alone, no field), and runs the real readers on both graphs:ingest_binding_context(the registry and the refusals),wall_facts(everySWallandArcWall) andfrom_graph(the catalog and its provenance JSON) on theGlobal/Latestdocument in full and projected.changelog.d/changed/1335-*.md: three fragments.What is not cut (floors)
Shares of the exports' own time over the 33 samples of 2015 to 2023 at the final head (profile run 42124471783, scratch head c85c73c, an EPYC 9V74, shares only), against
mainbefore in run 42111526405: definitions 6.8% (15.6%), the named-parameter read 3.4% (2.9%), the phase read 3.5% (3.1%), the record index 2.3% (1.9%), the wall records 1.8% (3.5%) and wall types 0.4%, the preamble 1.7% (4.0%), area regions 0.9% (0.8%); all native passes 22.8% (33.0%). If every one cost nothing, the export would take about 0.92 of 0.11.0's time (an estimate: those shares applied to the timing run's totals), so the 1.2 against 0.11.0 is what these passes still cost.rac_advanced's 2,207 (4.4%, run 42111526405). Projecting itsRoomElemgraphs (38 ms of it) measured 0.993 of this PR's first head's time (run 42117002359, closed as perf(reader): the phase read builds only the fields it keeps of each RoomElem graph (#1335) #1487): inside the run's noise, for a list kept by hand. The rest is the per-record selection and routing ofextract_records(about 26 ms) and the Element base read of 16,981 other records (18 ms), which builds anElementBaseand aRecordfor each so that the same closure sees what it saw.native_area_regions.rs, new since 0.11.0): on 2015rac_advancedtheLevelRoomPlangraphs are 9.5 ms decoding and 5.0 ms freeing and theCurveElemgraphs 5.9 and 1.6, about 22 ms of 1,646 (1.3%) at the final head (run 42124471783), and only a file with Areas pays it; over the 33 samples the pass is 0.9% of the exports' time in all. Its reader,plan(), buildsm_segments,m_circuits,m_componentsandm_roomsof thePlanTopology, which is the bulk of those graphs, so a projection would leave most of the 1.3%: not worth a pull request. A floor.rac_advanced), and the byte scan of the named-parameter read.Testing
Unit tests are banned here: none added. All runs are of 4140b96, against
mainat df96767 or the clean tags; Autodesk's sample projects, the reference models, the BIM4LCA offices, the Revit 2025 models and the held-out models are fetched for the runs and never stored (held-out as counts).-f base=main -f nordic=true -f r2025=true -f holdout=true -f samples=true -f timing=true, default scorers, noifc_flags): byte identity of every model and the timing againstmain, above. Itssame-project gatesjob wrote the check run 127599178067:success, "pass: 9 samples held to their earlier saves, and the Areas on 4 saves and on the 2021 save". The probe run below, dispatched on the same commit and finished 30 seconds later, wrote aneutral"NOT MEASURED: the scorers asked for exclude bodies_across_saves" (127599399222) that GitHub lists as the latest check of that name; the success is listed withfilter=all.-f probe=probe_1335_projected_graphs -f samples=true -f r2025=true -f scorers=none; every sample of 2015 to 2027, the reference models and the Revit 2025 models, 88 files): 1,128,043 records, 6,768,258 projected decodes under six lists, 0 mismatches; the 4,743 records the full decode refuses are refused alike (FamilyInstance3,403,VarSketch1,125,ImageHolder105,AppearanceAssetElem38,RebarShape30, others fewer). NoFamilyorFamilySymbolis among them: the registry the definitions pass builds is the same from both graphs on every file.wall_factsis the same on all 7,900SWallandArcWallrecords. The catalog's document gives the same catalog and provenance on 79 files and is refused alike on 9. Not measured: the BIM4LCA offices and the held-out models under the probe.Peak memory against 0.11.0 is 0.61 to 0.96 on the samples (the models 0.73 to 1.03), against 0.15.0 0.86 to 1.00 on the samples (the models 0.82 to 1.00).
main, the timing job of the unfiltered run (one runner per shard; shard 1 an EPYC 7763 for the 2024 samples, shard 2 a 9V74 for the samples to 2023 and the reference models, shard 3 a 7763 for the offices and the 2027 samples, shard 4 a 9V45 for the 2025 and 2026 samples):main(s)mainEarlier heads of the three parts (the decoder is the same; each slice measured against the one before it): the definitions part 0.919 of
mainover the 33 samples (run 42115512932, head fa10c6d), the wall records part 0.976 (run 42116989088, head 16527ce) and the preamble part 0.988 (run 42117016111, head 76e98d7). The wall records part raised the peak of 2019 and 2020rac_advancedfrom 168 to 173 MiB (1.03) in its run; the definitions part had taken them from 173 to 168, so they are at whatmainpeaks at.tools/check-local.sh: nothing is built on this Mac (no local compute); CI's fast jobs pass on this head.Audit-honesty checklist
changelog.d/changed/.keptand noduplicateunless the decode is projected.Breaking changes
None to the stable API, the CLIs or any export.
rvt::research(unstable):native_document::Optionsgainsgraph_fields(andGraphFields);native_parametersgainsdecode_graph_projected;native_wallsgainsWALL_READ_FIELDS,WallFactsandwall_facts;native_es_cataloggainsCATALOG_FIELDS,document,document_limitsandfrom_graph;native_parameter_definitionsgainsFAMILY_BINDING_FIELDS,SYMBOL_BINDING_FIELDSandbinding_context_fields.Related issues
Refs #1335, which stays open.