| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent e624bb5 commit ce6a865
3 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -4,14 +4,19 @@ const common = require('../common'); | |||
| 4 | 4 | const Writable = require('stream').Writable; | |
| 5 | 5 | ||
| 6 | 6 | const bench = common.createBenchmark(main, { | |
| 7 | - n: [2e6] | ||
| 7 | + n: [2e6], | ||
| 8 | + sync: ['yes', 'no'] | ||
| 8 | 9 | }); | |
| 9 | 10 | ||
| 10 | - function main({ n }) { | ||
| 11 | + function main({ n, sync }) { | ||
| 11 | 12 | const b = Buffer.allocUnsafe(1024); | |
| 12 | 13 | const s = new Writable(); | |
| 14 | + sync = sync === 'yes'; | ||
| 13 | 15 | s._write = function(chunk, encoding, cb) { | |
| 14 | - cb(); | ||
| 16 | + if (sync) | ||
| 17 | + cb(); | ||
| 18 | + else | ||
| 19 | + process.nextTick(cb); | ||
| 15 | 20 | }; | |
| 16 | 21 | ||
| 17 | 22 | bench.start(); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -137,6 +137,10 @@ function WritableState(options, stream, isDuplex) { | |||
| 137 | 137 | // The amount that is being written when _write is called. | |
| 138 | 138 | this.writelen = 0; | |
| 139 | 139 | ||
| 140 | + // Storage for data passed to the afterWrite() callback in case of | ||
| 141 | + // synchronous _write() completion. | ||
| 142 | + this.afterWriteTickInfo = null; | ||
| 143 | + | ||
| 140 | 144 | this.bufferedRequest = null; | |
| 141 | 145 | this.lastBufferedRequest = null; | |
| 142 | 146 | ||
@@ -483,22 +487,41 @@ function onwrite(stream, er) { | |||
| 483 | 487 | } | |
| 484 | 488 | ||
| 485 | 489 | if (sync) { | |
| 486 | - process.nextTick(afterWrite, stream, state, cb); | ||
| 490 | + // It is a common case that the callback passed to .write() is always | ||
| 491 | + // the same. In that case, we do not schedule a new nextTick(), but rather | ||
| 492 | + // just increase a counter, to improve performance and avoid memory | ||
| 493 | + // allocations. | ||
| 494 | + if (state.afterWriteTickInfo !== null && | ||
| 495 | + state.afterWriteTickInfo.cb === cb) { | ||
| 496 | + state.afterWriteTickInfo.count++; | ||
| 497 | + } else { | ||
| 498 | + state.afterWriteTickInfo = { count: 1, cb, stream, state }; | ||
| 499 | + process.nextTick(afterWriteTick, state.afterWriteTickInfo); | ||
| 500 | + } | ||
| 487 | 501 | } else { | |
| 488 | - afterWrite(stream, state, cb); | ||
| 502 | + afterWrite(stream, state, 1, cb); | ||
| 489 | 503 | } | |
| 490 | 504 | } | |
| 491 | 505 | } | |
| 492 | 506 | ||
| 493 | - function afterWrite(stream, state, cb) { | ||
| 507 | + function afterWriteTick({ stream, state, count, cb }) { | ||
| 508 | + state.afterWriteTickInfo = null; | ||
| 509 | + return afterWrite(stream, state, count, cb); | ||
| 510 | + } | ||
| 511 | + | ||
| 512 | + function afterWrite(stream, state, count, cb) { | ||
| 494 | 513 | const needDrain = !state.ending && !stream.destroyed && state.length === 0 && | |
| 495 | 514 | state.needDrain; | |
| 496 | 515 | if (needDrain) { | |
| 497 | 516 | state.needDrain = false; | |
| 498 | 517 | stream.emit('drain'); | |
| 499 | 518 | } | |
| 500 | - state.pendingcb--; | ||
| 501 | - cb(); | ||
| 519 | + | ||
| 520 | + while (count-- > 0) { | ||
| 521 | + state.pendingcb--; | ||
| 522 | + cb(); | ||
| 523 | + } | ||
| 524 | + | ||
| 502 | 525 | finishMaybe(stream, state); | |
| 503 | 526 | } | |
| 504 | 527 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,30 @@ | |||
| 1 | + 'use strict'; | ||
| 2 | + const common = require('../common'); | ||
| 3 | + const { Console } = require('console'); | ||
| 4 | + const { Writable } = require('stream'); | ||
| 5 | + const async_hooks = require('async_hooks'); | ||
| 6 | + | ||
| 7 | + // Make sure that repeated calls to console.log(), and by extension | ||
| 8 | + // stream.write() for the underlying stream, allocate exactly 1 tick object. | ||
| 9 | + // At the time of writing, that is enough to ensure a flat memory profile | ||
| 10 | + // from repeated console.log() calls, rather than having callbacks pile up | ||
| 11 | + // over time, assuming that data can be written synchronously. | ||
| 12 | + // Refs: https://github.com/nodejs/node/issues/18013 | ||
| 13 | + // Refs: https://github.com/nodejs/node/issues/18367 | ||
| 14 | + | ||
| 15 | + const checkTickCreated = common.mustCall(); | ||
| 16 | + | ||
| 17 | + async_hooks.createHook({ | ||
| 18 | + init(id, type, triggerId, resoure) { | ||
| 19 | + if (type === 'TickObject') checkTickCreated(); | ||
| 20 | + } | ||
| 21 | + }).enable(); | ||
| 22 | + | ||
| 23 | + const console = new Console(new Writable({ | ||
| 24 | + write: common.mustCall((chunk, encoding, cb) => { | ||
| 25 | + cb(); | ||
| 26 | + }, 100) | ||
| 27 | + })); | ||
| 28 | + | ||
| 29 | + for (let i = 0; i < 100; i++) | ||
| 30 | + console.log(i); | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments