First, please note that this message was generated with AI and I thought it would be useful to share it with you. If you're not comfortable receiving AI-generated messages like this, please let me know, and I'll stop using this approachin the future.
While building RingScript' WebAssembly runtime of the Ring 1.27 VM, I ran into a
performance behaviour I think is worth sharing. Everything below is
measured on
stock `ring.exe`
1.27.0 on Windows — it is not a
WebAssembly issue.
The measurement. Both loops below do the same work — 20,000 calls to
`
len()`, which only reads a stored size. Only the size of the argument
differs:
cTiny = "0123456789"
cBig = "a"
while len(cBig) < 1048576 cBig += cBig end
t1 = clock()
nS = 0
for i = 1 to 20000 nS += len(cTiny) next
t2 = clock()
nS2 = 0
for i = 1 to 20000 nS2 += len(cBig) next
t3 = clock()
see "len(10 B) x 20k : " + ((t2-t1)/clockspersecond()*1000) + " ms" + nl
see "len(1 MB) x 20k : " + ((t3-t2)/clockspersecond()*1000) + " ms" + nl
Result:
1 ms vs about 4,900–5,000 ms across four runs. Roughly 20 GB
was copied to answer 20,000 length queries.
The cause is `RING_VM_STACK_PUSHCVAR` in `vm.h`: passing a string
variable to a function copies the whole value onto the VM stack. That is
also what gives Ring its clean value semantics, so it is a trade-off
rather than a bug — but it means any Ring code that scans a large string
is O(length) per touch and O(n²) overall. Parsers, tokenizers, template
engines and text processing all inherit it. A JSON decoder written in
pure Ring reaches about 0.27 MB/s for this reason.
Three possible directions, in rising order of ambition:
1.
Borrowed arguments for read-only builtins — `len()`, `ascii()`,
`left()`, `right()`, `substr()`, `find()` never mutate or retain
their string argument, so a flag at C-function registration could let
PUSHCVAR pass a pointer for exactly those. Smallest change, and it
covers the common cases.
2.
Copy-on-write strings — reference-count the buffer, copy on write.
Helps user-defined functions too, but needs care with the GC.
3.
Reuse the existing `RING_OBJTYPE_SUBSTRING` machinery, which already
expresses "a view into a string".
I am not proposing a patch — the trade-off is yours to weigh.
Best regards,
Mansour