Repository navigation
Share list storage between prefixes so concat appends in place - #225
Merged
Merged
Conversation
A list is now a view of the first n elements of a buffer that longer lists may share. concat(acc, ...) extends acc's buffer in place when acc ends at the buffer's frontier and there is room (claiming the slots with one compare-and-swap), and otherwise copies -- into double the room if the list came from appending before, exactly sized if not. Old lists stay valid whatever still holds them, so this works through the interpreter's retained call chain too. The idiomatic tail-recursive accumulator, f(i, concat(acc, [x])), goes from quadratic to linear: a million appends in 0.44 s against an estimated 40 minutes, and the script in BelfrySCAD#668 now reaches the recursion limit in 1.5 s. ListItems keeps the old vector's read interface; its conversion to std::vector is explicit so no copy happens by accident. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 2, 2026
Merged
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.
Refs BelfrySCAD/BelfrySCAD#668
Problem.
concatcopied its whole first argument every time. That made the idiomatic tail-recursive accumulatorf(i, concat(acc, [x]))quadratic: 40k appends took 3.6 s, and a million would take an estimated ~40 minutes.BelfrySCAD#668's script loops forever by mistake. It looked like recursion detection was broken, but the million-iteration tail-call cap does fire; it would just take far longer than anyone waits to get there. OpenSCAD avoids this with lazy concatenation:
concatembeds its argument vectors and flattens them on first index.Change. A list is now a view of the first n elements of a buffer that longer lists may share (
ListItems,ListBuffer,listAppendinvalue.hpp/value.cpp).concat(acc, …)extendsacc's buffer in place whenaccends where the buffer's used part ends and there is spare capacity. One compare-and-swap claims the new slots, so two lists sharing a prefix can never write the same slot; the loser copies.concat(a, b)costs no spare memory.ListItemskeeps the old vector's read interface (size,[], iterators,data). Its conversion tostd::vectorisexplicit, so a copy can never be made by accident binding aconst std::vector&. Every such site was found by the compiler and moved toconst ListItems&.Results (Release build with TBB; baseline is
mainbuilt with the same options):lenOSCAD_BYTECODE_VM=0), memoryBOSL2 timings, best of 5:
resample_path/path_cut_points/offset−9%,skin−8%, the reporter's text-wrapping library −8%,threaded_rod−15%.spur_gearand roundedcuboid+1–2% when runs are interleaved. That's the extra allocation per list (list header, shared buffer header, element storage); storing elements inline with the buffer would remove it.Tested
ctest: 2013 passed, including 5 newListAppendtests:tests/test_python_bindings.py: 61 passed, against a wheel built from this branch. I confirmed the new code was loaded by timing 1M appends through BelfrySCAD (1.0 s).Not done here:
[each acc, x]append form still copies. It goes through the comprehension accumulator, notconcat.🤖 Generated with Claude Code