Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor test-readline-async-iterators into a benchmark #49237

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 36 additions & 2 deletions benchmark/readline/readline-iterable.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const { Readable } = require('stream');

const bench = common.createBenchmark(main, {
n: [1e1, 1e2, 1e3, 1e4, 1e5, 1e6],
type: ['old', 'new'],
});

const loremIpsum = `Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed
Expand All @@ -21,6 +22,37 @@ Condimentum mattis pellentesque id nibh tortor id aliquet lectus proin.
Diam in arcu cursus euismod quis viverra nibh.
Rest of line`;

function oldWay() {
const readable = new Readable({
objectMode: true,
read: () => {
this.resume();
},
destroy: (err, cb) => {
this.off('line', lineListener);
this.off('close', closeListener);
this.close();
cb(err);
},
});
const lineListener = (input) => {
if (!readable.push(input)) {
// TODO(rexagod): drain to resume flow
this.pause();
}
};
const closeListener = () => {
readable.push(null);
};
const errorListener = (err) => {
readable.destroy(err);
};
this.on('error', errorListener);
this.on('line', lineListener);
this.on('close', closeListener);
return readable[Symbol.asyncIterator]();
}

function getLoremIpsumStream(repetitions) {
const readable = Readable({
objectMode: true,
Expand All @@ -32,16 +64,18 @@ function getLoremIpsumStream(repetitions) {
return readable;
}

async function main({ n }) {
async function main({ n, type }) {
bench.start();
let lineCount = 0;

const iterable = readline.createInterface({
input: getLoremIpsumStream(n),
});

const readlineIterable = type === 'old' ? oldWay.call(iterable) : iterable;

// eslint-disable-next-line no-unused-vars
for await (const _ of iterable) {
for await (const _ of readlineIterable) {
lineCount++;
}
bench.end(lineCount);
Expand Down
110 changes: 0 additions & 110 deletions test/parallel/test-readline-async-iterators.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,115 +73,6 @@ async function testMutual() {
}
}

async function testPerformance() {
const loremIpsum = `Lorem ipsum dolor sit amet, consectetur adipiscing elit,
sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Dui accumsan sit amet nulla facilisi morbi tempus iaculis urna.
Eget dolor morbi non arcu risus quis varius quam quisque.
Lacus viverra vitae congue eu consequat ac felis donec.
Amet porttitor eget dolor morbi non arcu.
Velit ut tortor pretium viverra suspendisse.
Mauris nunc congue nisi vitae suscipit tellus.
Amet nisl suscipit adipiscing bibendum est ultricies integer.
Sit amet dictum sit amet justo donec enim diam.
Condimentum mattis pellentesque id nibh tortor id aliquet lectus proin.
Diam in arcu cursus euismod quis viverra nibh.
`;

const REPETITIONS = 10000;
const SAMPLE = 100;
const THRESHOLD = 81;

function getLoremIpsumStream() {
const readable = Readable({
objectMode: true,
});
let i = 0;
readable._read = () => readable.push(
i++ >= REPETITIONS ? null : loremIpsum
);
return readable;
}

function oldWay() {
const readable = new Readable({
objectMode: true,
read: () => {
this.resume();
},
destroy: (err, cb) => {
this.off('line', lineListener);
this.off('close', closeListener);
this.close();
cb(err);
},
});
const lineListener = (input) => {
if (!readable.push(input)) {
// TODO(rexagod): drain to resume flow
this.pause();
}
};
const closeListener = () => {
readable.push(null);
};
const errorListener = (err) => {
readable.destroy(err);
};
this.on('error', errorListener);
this.on('line', lineListener);
this.on('close', closeListener);
return readable[Symbol.asyncIterator]();
}

function getAvg(mean, x, n) {
return (mean * n + x) / (n + 1);
}

let totalTimeOldWay = 0;
let totalTimeNewWay = 0;
let totalCharsOldWay = 0;
let totalCharsNewWay = 0;
const linesOldWay = [];
const linesNewWay = [];

for (let time = 0; time < SAMPLE; time++) {
const rlOldWay = readline.createInterface({
input: getLoremIpsumStream(),
});
let currenttotalTimeOldWay = Date.now();
for await (const line of oldWay.call(rlOldWay)) {
totalCharsOldWay += line.length;
if (time === 0) {
linesOldWay.push(line);
}
}
currenttotalTimeOldWay = Date.now() - currenttotalTimeOldWay;
totalTimeOldWay = getAvg(totalTimeOldWay, currenttotalTimeOldWay, SAMPLE);

const rlNewWay = readline.createInterface({
input: getLoremIpsumStream(),
});
let currentTotalTimeNewWay = Date.now();
for await (const line of rlNewWay) {
totalCharsNewWay += line.length;
if (time === 0) {
linesNewWay.push(line);
}
}
currentTotalTimeNewWay = Date.now() - currentTotalTimeNewWay;
totalTimeNewWay = getAvg(totalTimeNewWay, currentTotalTimeNewWay, SAMPLE);
}

assert.strictEqual(totalCharsOldWay, totalCharsNewWay);
assert.strictEqual(linesOldWay.length, linesNewWay.length);
linesOldWay.forEach((line, index) =>
assert.strictEqual(line, linesNewWay[index])
);
const percentage = totalTimeNewWay * 100 / totalTimeOldWay;
assert.ok(percentage <= THRESHOLD, `Failed: ${totalTimeNewWay} isn't lesser than ${THRESHOLD}% of ${totalTimeOldWay}. Actual percentage: ${percentage.toFixed(2)}%`);
}

async function testSlowStreamForLeaks() {
const message = 'a\nb\nc\n';
const DELAY = 1;
Expand Down Expand Up @@ -225,6 +116,5 @@ async function testSlowStreamForLeaks() {

testSimple()
.then(testMutual)
.then(testPerformance)
.then(testSlowStreamForLeaks)
.then(common.mustCall());
Loading