joyent/node — pull request #8826 by chrisdickinson (2014-12-04 23:28:56+00:00)
Title: stream: switch _writableState.buffer to queue

In cases where many small writes are made to a stream lacking `_writev`, the array data structure backing the `WriteReq` buffer would greatly increase GC pressure.

Specifically, in the `fs.WriteStream` case, the `clearBuffer` routine would only clear a single `WriteReq` from the buffer before exiting, but would cause the entire backing array to be GC'd. Switching to `[].shift` lessened pressure, but still the bulk of the time was spent in `memcpy`.

This replaces that structure with a linked list-backed queue so that adding and removing from the queue is O(1). In the `_writev` case, collecting the buffer requires an O(N) loop over the buffer, but that was already being performed to collect callbacks, so slowdown should be neglible.

Example code (courtesy @hayes, who originally discovered this slowdown while dealing with log output to bunyan):

```javascript
var fs = require('fs')
var stream = fs.createWriteStream('/dev/null', {flags: 'w+'})
var buf = new Buffer('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n', 'utf8')
var start = Date.now()
var written = 0
function log() {
for(var i = 0; i < 1000; i++) {
++written
stream.write(buf, function() {
--written
})
}
if(Date.now() - start < 1000) {
return setImmediate(log)
}
setInterval(function() {
console.log(written, process.memoryUsage())
}, 1000).unref()
console.log('done writing')
}
log()
```

### before the change:

[![before](http://neversaw.us/media/stacks.svg)](http://neversaw.us/media/stacks.svg)

### array.shift()

(this replaces the common c=1 slice case with `[].shift()`):

[![before](http://neversaw.us/media/array-shift.svg)](http://neversaw.us/media/array-shift.svg)

### as queue:

[![after](http://neversaw.us/media/after.svg)](http://neversaw.us/media/after.svg)

I'm working on getting a better "after" flamegraph at the moment.

As a measurable effect: the current version clears ~300 or so WriteReqs/second on my machine; the queue version clears ~50k WriteReqs/second.

cc @wraithan

R=@trevnorris @misterdjules