Skip to content

perf: patch midi-file writeBytes to in-place push loop - #79

Closed
elicwhite wants to merge 1 commit into
graphite-base/79from
perf/midi-writebytes-inplace
Closed

perf: patch midi-file writeBytes to in-place push loop#79
elicwhite wants to merge 1 commit into
graphite-base/79from
perf/midi-writebytes-inplace

Conversation

@elicwhite

Copy link
Copy Markdown
Owner

Writer.prototype.writeBytes was O(n²):
this.buffer = this.buffer.concat(Array.prototype.slice.call(arr, 0))
Every call allocated a new array and replaced this.buffer, so writing N
bytes via M calls cost O(N·M) time and churned the GC.

Replace with an in-place push loop — each call is O(arr.length), total
write is O(N).

Measured on the writer autoresearch bench (2000 charts, 500 .chart +
1500 .mid, 8 workers):

  • mean: 90.115 ms → 6.721 ms (13.4× faster)
  • p50: 59.944 ms → 5.764 ms (10.4× faster)
  • p95: 274.443 ms → 17.255 ms (15.9× faster)
  • p99: 560.784 ms → 25.263 ms (22.2× faster)
  • wall: 24.972s → 5.496s (4.5× faster)
  • summed writer time: 162.206s → 12.098s (93% reduction)

0 hash mismatches, 442/442 tests still green — byte-identical output.

CPU profile at baseline showed:
35.76% Writer.writeUInt8
21.06% Writer.writeBytes
13.81% GC
of total writer time. Fixing writeBytes removes both the direct cost
(it's a handful of the 19 writeBytes callsites downstream of every
track write) and the GC pressure from the allocate-and-replace pattern.

Writer.prototype.writeBytes was O(n²):
  this.buffer = this.buffer.concat(Array.prototype.slice.call(arr, 0))
Every call allocated a new array and replaced `this.buffer`, so writing N
bytes via M calls cost O(N·M) time and churned the GC.

Replace with an in-place push loop — each call is O(arr.length), total
write is O(N).

Measured on the writer autoresearch bench (2000 charts, 500 .chart +
1500 .mid, 8 workers):
  - mean:  90.115 ms → 6.721 ms   (13.4× faster)
  - p50:   59.944 ms → 5.764 ms   (10.4× faster)
  - p95:  274.443 ms → 17.255 ms  (15.9× faster)
  - p99:  560.784 ms → 25.263 ms  (22.2× faster)
  - wall:  24.972s   → 5.496s     (4.5× faster)
  - summed writer time: 162.206s → 12.098s (93% reduction)

0 hash mismatches, 442/442 tests still green — byte-identical output.

CPU profile at baseline showed:
  35.76% Writer.writeUInt8
  21.06% Writer.writeBytes
  13.81% GC
of total writer time. Fixing writeBytes removes both the direct cost
(it's a handful of the 19 writeBytes callsites downstream of every
track write) and the GC pressure from the allocate-and-replace pattern.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant