diff --git a/benchmark/process/next-tick-loop.js b/benchmark/process/next-tick-loop.js new file mode 100644 index 00000000000000..36f877274a5949 --- /dev/null +++ b/benchmark/process/next-tick-loop.js @@ -0,0 +1,30 @@ +'use strict'; +const common = require('../common.js'); +const bench = common.createBenchmark(main, { + n: [1e4, 2e4, 4e4], + loop: [1e3], +}); + +function main({ n, loop }) { + bench.start(); + run(); + function run() { + let j = 0; + + function cb() { + j++; + if (j === n) { + loop--; + if (loop === 0) { + bench.end(n); + } else { + run(); + } + } + } + + for (let i = 0; i < n; i++) { + process.nextTick(cb); + } + } +} diff --git a/lib/internal/fixed_queue.js b/lib/internal/fixed_queue.js index f6f3110d8ec5d4..9c8b4a40d33fc3 100644 --- a/lib/internal/fixed_queue.js +++ b/lib/internal/fixed_queue.js @@ -78,18 +78,18 @@ class FixedCircularBuffer { } shift() { - const nextItem = this.list[this.bottom]; - if (nextItem === undefined) + if (this.isEmpty()) return null; + const data = this.list[this.bottom]; this.list[this.bottom] = undefined; this.bottom = (this.bottom + 1) & kMask; - return nextItem; + return data; } } module.exports = class FixedQueue { constructor() { - this.head = this.tail = new FixedCircularBuffer(); + this.head = this.tail = this.buffer = new FixedCircularBuffer(); } isEmpty() { @@ -97,21 +97,29 @@ module.exports = class FixedQueue { } push(data) { - if (this.head.isFull()) { - // Head is full: Creates a new queue, sets the old queue's `.next` to it, - // and sets it as the new main queue. - this.head = this.head.next = new FixedCircularBuffer(); + const head = this.head; + if (head.isFull()) { + // Head is full: Reuses the empty tail or creates a new queue, + // sets the old queue's `.next` to it, and sets it as the new main queue. + const buffer = this.buffer; + if (buffer.isEmpty()) { + this.buffer = buffer.next; + buffer.next = null; + this.head = head.next = buffer; + } else { + this.head = head.next = new FixedCircularBuffer(); + } } this.head.push(data); } shift() { const tail = this.tail; - const next = tail.shift(); + const data = tail.shift(); if (tail.isEmpty() && tail.next !== null) { // If there is another queue, it forms the new tail. this.tail = tail.next; } - return next; + return data; } }; diff --git a/test/parallel/test-fixed-queue.js b/test/parallel/test-fixed-queue.js index a50be1309a5ea8..5743f7d458d398 100644 --- a/test/parallel/test-fixed-queue.js +++ b/test/parallel/test-fixed-queue.js @@ -13,7 +13,11 @@ const FixedQueue = require('internal/fixed_queue'); queue.push('a'); assert(!queue.isEmpty()); assert.strictEqual(queue.shift(), 'a'); + assert.strictEqual(queue.head, queue.tail); + assert.strictEqual(queue.head.next, null); assert.strictEqual(queue.shift(), null); + assert.strictEqual(queue.head, queue.tail); + assert.strictEqual(queue.head.next, null); } { @@ -23,11 +27,14 @@ const FixedQueue = require('internal/fixed_queue'); assert(queue.head.isFull()); queue.push('a'); assert(!queue.head.isFull()); - assert.notStrictEqual(queue.head, queue.tail); + assert.strictEqual(queue.head, queue.tail.next); + assert.strictEqual(queue.head.next, null); + for (let i = 0; i < 2047; i++) assert.strictEqual(queue.shift(), 'a'); assert.strictEqual(queue.head, queue.tail); + assert.strictEqual(queue.head.next, null); assert(!queue.isEmpty()); assert.strictEqual(queue.shift(), 'a'); assert(queue.isEmpty());