Stream: performance

Topic: Repeated copying of unchanged record fields in a recursion


view this post on Zulip zhen (Nov 03 2026 at 14:59):

In the process of optimizing my project, I (and a LLM) found that I had to manually split a recursion function into a non-recur cold path + a recursive helper in order to reduce volume of direct copy:

image.png

I wouldnt call this a bug, but I do think this can be optimized into generating same bytecode(?)

Also, via LLMs disassembly, the splitted version will result larger on stack allocation, however for n iterations, the copies in terms of bytes decreases from (2n+2) * 1016 + 2n*779 to (2n+2) * 1015 + 1015 * n.

The code below is a minimal PoC generated by a LLM from my repo.

app [main!] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.26.0/GddgvsGjiNXrHFW6au5i5dUDbTs8FinQdrgE2Gqmw53t.tar.zst" }
import pf.OsStr
import pf.Stdout

Quad : { a : U64, b : U64, c : U64, d : U64 }
Block : { a : Quad, b : Quad, c : Quad, d : Quad }
Regs : { a : Block, b : Block, c : Block, d : Block }
Pad : { a : Regs, b : Block, c : Block, d : Quad }
Bus := { bytes : List(U8), l1 : List(U8), l2 : List(U8), l3 : List(U8), l4 : List(U8), l5 : List(U8), l6 : List(U8), l7 : List(U8), l8 : List(U8), regs : Pad }
Machine :: { bus : Bus, now : U64, retained : List(U8) }

view = |m| {
    bus: { bytes: m.bus.bytes, l1: m.bus.l1, l2: m.bus.l2, l3: m.bus.l3, l4: m.bus.l4,
        l5: m.bus.l5, l6: m.bus.l6, l7: m.bus.l7, l8: m.bus.l8, regs: m.bus.regs },
    now: m.now, retained: m.retained,
}

make : U64 -> Machine
make = |seed| {
    q = { a: seed, b: seed.plus_wrap(1), c: seed.plus_wrap(2), d: seed.plus_wrap(3) }
    block = { a: q, b: q, c: q, d: q }
    regs = { a: { a: block, b: block, c: block, d: block }, b: block, c: block, d: q }
    bytes = List.repeat(seed.to_u8_wrap(), 16)
    { bus: { bytes, l1: bytes, l2: bytes, l3: bytes, l4: bytes, l5: bytes, l6: bytes, l7: bytes, l8: bytes, regs }, now: seed, retained: bytes }
}
advance : Machine, U64 -> Try(Machine, [Backwards, OutOfBounds])
advance = |m, target| {
    if target < m.now { Err(Backwards) }
    else if target - m.now > 2 {
        changed = m.bus.bytes.set(0, m.bus.bytes.get(0)?.plus_wrap(1))?
        advance({ ..m, now: m.now + 2, bus: { ..m.bus, bytes: changed } }, target)
    } else { Ok({ ..m, now: target }) }
}

# The splitted version, performs a bit better
# advance : Machine, U64 -> Try(Machine, [Backwards, OutOfBounds])
# advance = |m, target| {
#     if target < m.now { Err(Backwards) }
#    else if target - m.now > 2 { advance_events(m, target) }
#     else { Ok({ ..m, now: target }) }
# }
# advance_events : Machine, U64 -> Try(Machine, [Backwards, OutOfBounds])
# advance_events = |m, target| {
#     if target < m.now { Err(Backwards) }
#    else if target - m.now > 2 {
#        changed = m.bus.bytes.set(0, m.bus.bytes.get(0)?.plus_wrap(1))?
#        advance_events({ ..m, now: m.now + 2, bus: { ..m.bus, bytes: changed } }, target)
#    } else { Ok({ ..m, now: target }) }
# }

candidate : Machine, U64, U64 -> Try(Machine, [Backwards, OutOfBounds])
candidate = |m, step, count| {
    if count == 0 { Ok(m) }
    else { candidate(advance(m, m.now.plus_wrap(step))?, step, count - 1) }
}
result_view = |r| match r { Ok(m) => Ok(view(m)) Err(e) => Err(e) }

exercise! : Machine, U64 => Try({}, _)
exercise! = |m, i| {
    if i == 24 { return Ok({}) }
    step = i.mod_by(12)
    a = candidate(m, step, i)
    b = candidate(m, step, i + 1)
    Stdout.line!(Str.inspect((result_view(a), result_view(b))))?
    exercise!(m, i + 1)
}
main! : List(OsStr) => Try({}, _)
main! = |args| {
    seed = U64.from_str(OsStr.to_str_try(args.get(0)?)?)?
    m = advance(make(seed), seed)?
    exercise!(m, 0)?
    Stdout.line!(Str.inspect(view(m)))?
    Ok({})
}

view this post on Zulip Anton (Nov 03 2026 at 15:18):

Hi @zhen,
Could you make a GitHub issue for this?

view this post on Zulip zhen (Nov 03 2026 at 15:27):

Anton said:

Hi @zhen,
Could you make a GitHub issue for this?

Created.
https://github.com/roc-lang/roc/issues/12826


Last updated: Nov 04 2026 at 18:54 UTC