Summary
Follow-up to #836, after the Ownership section (thanks: that one owned use was the whole cost). The borrowed tree read took the edited world from 100–110 ms to 26 ms at 512², but the read still doubles the frame once the world has edits (13 → 26 ms), and the same frame drawn from a flat buffer costs 1.3 ms edited or not. An Array is that flat buffer, and the runtime's get opens no half, but a bang cannot borrow one: w is "consumed more than once" on the two sides of a parallel let, +w is "expected Data, observed Type", and the guide says so ("An Array has one owner, so it cannot go down a fork tree: build lists"). The question is whether a read-only borrow of an Array by a bang — host-owned, borrowed by the bang and returned, as the scene is — fits the runtime, or is ruled out by something I do not see.
What I did
The program is gfx/05_craft.bend in AdrielSantana/metal-bending: a 32×32×32 editable voxel world, fork!(7n) over 4×4 tiles at 512², the world a terrain function plus a quadtree of edits, read by one recursive wget that matches and recurses into one field (the guide's rule; in the emitted C the read is term_peek only). Windowless bench: five frames, the camera turning 0.01 rad a frame, a total(9n) checksum per frame, the ten checksums identical across every row below; "built" is 300 blocks placed in one corner. Apple M5; Bend 2.0.16 and 2.0.17 give the same numbers.
| 512², Metal |
untouched |
300 blocks placed |
the read that returned the chosen node (Bool.pick), before #836's answer |
15 ms |
100–110 ms |
borrowed recursive walk (term_peek only) |
13 ms |
25–29 ms |
What remains in the edited world is the walk itself: five dependent reads per column crossing (five levels for 32×32 columns), where an untouched world is one WNone match plus the terrain function.
To see what a flat read is worth, I ran the same pixel function on WebGPU (the leaf translated by hand into WGSL, on the runtime shape described in the "WebGPU, measured" section of #866), with the world as a 1024-word buffer, one word of 32 height bits per column, 0xFFFFFFFF for "not edited, compute the terrain". The frame's checksum, computed as total(9n) does (a collapsed 2×2 tile counts once), equals the Bend program's on both worlds — 751552256 untouched, 351470114 built — so it is the same picture:
| 512², WebGPU, flat buffer |
untouched |
300 blocks placed |
| the same pixel function |
1.34 ms |
1.40 ms |
So in the Bend build the tree read is most of the frame and all of the edit cost; a flat indexed read makes edits free, as in a conventional voxel engine.
What the checker says
import Base
def at(+i: U32, wv: Array<U32> & U32) -> U32:
(w2, v) = wv
v
def sum4(+k: Nat, w: Array<U32>, +i: U32) -> U32:
match k:
case 0n:
at(i, Array.get(U32, w, i))
case 1n+j:
a b = sum4(j, w, (i * 2 : U32)) sum4(j, w, (i * 2 + 1 : U32))
(a + b : U32)
def main() -> IO(Unit):
do IO<Unit>:
+w : Array<U32> = Array.new(U32, 10n, 3)
IO.print(U32.show(sum4!(4n, w, 0)))
- expected : w
- observed : w (consumed more than once)
With +w: Array<U32>:
- expected : Data
- observed : Type
Both are right by the guide's rules (at drops w2, which is what a read-only use would want anyway).
The question
The Ownership section says the compiler borrows a boxed parameter "(not an Array)" that a def only matches or passes on, and the emitter's own note says get, set, swap, size and new open no half, so an array is a flat block that lanes could index. Is a read-only borrow of an Array inside a bang — the bang takes the block's address, every lane reads Array.get in place with no count and no split, nothing writes it until the bang returns it to the host, the way bend3d's scene is host-owned, borrowed and returned — something the runtime could support, or is there a reason it cannot (the block classes, the CUDA fault-per-page case, the checker's Type/Data split)? If it cannot, is the guide's per-tile list the intended shape for a DDA too, where a ray crosses a few dozen columns and the list would be walked per crossing? A sentence in the guide either way settles where an editable voxel world stops in Bend today; the numbers above are the case.
Written by Claude (Anthropic) with Adriel Santana driving; the numbers are from his M5.
Summary
Follow-up to #836, after the Ownership section (thanks: that one owned use was the whole cost). The borrowed tree read took the edited world from 100–110 ms to 26 ms at 512², but the read still doubles the frame once the world has edits (13 → 26 ms), and the same frame drawn from a flat buffer costs 1.3 ms edited or not. An
Arrayis that flat buffer, and the runtime'sgetopens no half, but a bang cannot borrow one:wis "consumed more than once" on the two sides of a parallel let,+wis "expected Data, observed Type", and the guide says so ("AnArrayhas one owner, so it cannot go down a fork tree: build lists"). The question is whether a read-only borrow of anArrayby a bang — host-owned, borrowed by the bang and returned, as the scene is — fits the runtime, or is ruled out by something I do not see.What I did
The program is
gfx/05_craft.bendin AdrielSantana/metal-bending: a 32×32×32 editable voxel world,fork!(7n)over 4×4 tiles at 512², the world a terrain function plus a quadtree of edits, read by one recursivewgetthat matches and recurses into one field (the guide's rule; in the emitted C the read isterm_peekonly). Windowless bench: five frames, the camera turning 0.01 rad a frame, atotal(9n)checksum per frame, the ten checksums identical across every row below; "built" is 300 blocks placed in one corner. Apple M5; Bend 2.0.16 and 2.0.17 give the same numbers.Bool.pick), before #836's answerterm_peekonly)What remains in the edited world is the walk itself: five dependent reads per column crossing (five levels for 32×32 columns), where an untouched world is one
WNonematch plus the terrain function.To see what a flat read is worth, I ran the same pixel function on WebGPU (the leaf translated by hand into WGSL, on the runtime shape described in the "WebGPU, measured" section of #866), with the world as a 1024-word buffer, one word of 32 height bits per column, 0xFFFFFFFF for "not edited, compute the terrain". The frame's checksum, computed as
total(9n)does (a collapsed 2×2 tile counts once), equals the Bend program's on both worlds — 751552256 untouched, 351470114 built — so it is the same picture:So in the Bend build the tree read is most of the frame and all of the edit cost; a flat indexed read makes edits free, as in a conventional voxel engine.
What the checker says
With
+w: Array<U32>:Both are right by the guide's rules (
atdropsw2, which is what a read-only use would want anyway).The question
The Ownership section says the compiler borrows a boxed parameter "(not an
Array)" that a def only matches or passes on, and the emitter's own note saysget,set,swap,sizeandnewopen no half, so an array is a flat block that lanes could index. Is a read-only borrow of anArrayinside a bang — the bang takes the block's address, every lane readsArray.getin place with no count and no split, nothing writes it until the bang returns it to the host, the waybend3d's scene is host-owned, borrowed and returned — something the runtime could support, or is there a reason it cannot (the block classes, the CUDA fault-per-page case, the checker'sType/Datasplit)? If it cannot, is the guide's per-tile list the intended shape for a DDA too, where a ray crosses a few dozen columns and the list would be walked per crossing? A sentence in the guide either way settles where an editable voxel world stops in Bend today; the numbers above are the case.Written by Claude (Anthropic) with Adriel Santana driving; the numbers are from his M5.