Repository navigation
Conversation
Under the threads scheduler, resetting a timer that had fired, or one that had been stopped, allocated a new timerNode because the old one had left the queue. Code that rearms one timer in a loop, such as a deadline on every network wait, allocated every time. Keep up to 256 finished or stopped nodes on a free list under the timer queue lock, and take from it in NewTimer and Reset. timerCallback keeps the timer pointer before reAddTimer, since the node may be reused as soon as that returns. Fixes tinygo-org#5957
|
I think an equivalent change should be made to the other schedulers, not just threads. We should also include aging logic similar to #6093 (although using an actual sync.Pool here is overkill) so the extra timer nodes don't hang around for ever. If the aging logic complicates things too much, we could have it cleared every GC() similar to how sync.Pool used to work on Big Go. And maybe for other systems, have a smaller cap than the current 256. Other than that, I like this change. Seems like a small code size increase for fewer allocations. |
The free list only covered the threads scheduler. Move it to shared code so the tasks, asyncify and cores schedulers reuse nodes too. Each scheduler supplies lockTimers, which takes the lock that already guards its timer queue. Drop the list after each collection, as sync.Pool did before Go 1.13, so a burst of timers does not keep its nodes forever. This reads the collection count that tinygo-org#6093 adds for sync.Pool, with the same files. Two collection aging would need a second list, and a dropped node is cheap to allocate again. Keep up to 256 nodes on hosted targets and 16 on baremetal ones.
|
Thanks. Node reuse now lives in shared code for every scheduler, and the list is dropped after each collection using the GC count from #6093, with a cap of 16 on baremetal and 256 elsewhere. The no-alloc test and a new drop-after-GC test pass under threads and tasks on linux/amd64, arm64 (qemu) and js/wasm, and testdata/timers.go matches on those plus a cortex-m-qemu and riscv-qemu (cores) build check. A cortex-m-qemu program using timers grows by about 170 bytes at -Oz, and the binary-size table is unchanged. |
Under the threads scheduler, resetting a timer that had fired or been stopped allocated a new timerNode every time. This keeps up to 256 finished or stopped nodes on a free list under the timer queue lock and reuses them in NewTimer and Reset. Other schedulers keep plain allocation.
On linux/amd64 a Stop and Reset loop goes from one allocation per iteration to none. The new TestTimerResetAfterStopNoAlloc in src/runtime fails on dev and passes here. testdata/timers.go matches its expected output with
-scheduler=threadsand-scheduler=tasks, on arm64 under qemu, and on js/wasm under node, and TestBuild/Host/timers.go passes.This touches the same code as #6035, so whichever lands second needs a small rebase.
Fixes #5957