You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
156 lines
4.5 KiB
156 lines
4.5 KiB
4 years ago
|
/**
|
||
|
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||
|
*
|
||
|
* This source code is licensed under the MIT license found in the
|
||
|
* LICENSE file in the root directory of this source tree.
|
||
|
*/
|
||
|
|
||
|
// Make sure to run node with --expose-gc option!
|
||
|
|
||
|
// The times are reliable if about 1% relative mean error if you run it:
|
||
|
|
||
|
// * immediately after restart
|
||
|
// * with 100% battery charge
|
||
|
// * not connected to network
|
||
|
|
||
|
/* eslint import/no-extraneous-dependencies: "off" */
|
||
|
|
||
|
const Benchmark = require('benchmark');
|
||
|
|
||
|
const diffBaseline = require('diff').diffLines;
|
||
|
const diffImproved = require('../build/index.js').default;
|
||
|
|
||
|
const testBaseline = (a, b) => {
|
||
|
const benchmark = new Benchmark({
|
||
|
fn() {
|
||
|
diffBaseline(a, b);
|
||
|
},
|
||
|
name: 'baseline',
|
||
|
onCycle() {
|
||
|
global.gc(); // after run cycle
|
||
|
},
|
||
|
onStart() {
|
||
|
global.gc(); // when benchmark starts
|
||
|
},
|
||
|
});
|
||
|
|
||
|
benchmark.run({async: false});
|
||
|
|
||
|
return benchmark.stats;
|
||
|
};
|
||
|
|
||
|
const testImproved = function(a, b) {
|
||
|
const benchmark = new Benchmark({
|
||
|
fn() {
|
||
|
// Split string arguments to make fair comparison with baseline.
|
||
|
const aItems = a.split('\n');
|
||
|
const bItems = b.split('\n');
|
||
|
|
||
|
const isCommon = (aIndex, bIndex) => aItems[aIndex] === bItems[bIndex];
|
||
|
|
||
|
// This callback obviously does less than baseline `diff` package,
|
||
|
// but avoiding double work and memory churn is the goal.
|
||
|
// For example, `jest-diff` has had to split strings that `diff` joins.
|
||
|
const foundSubsequence = () => {};
|
||
|
|
||
|
diffImproved(aItems.length, bItems.length, isCommon, foundSubsequence);
|
||
|
},
|
||
|
name: 'improved',
|
||
|
onCycle() {
|
||
|
global.gc(); // after run cycle
|
||
|
},
|
||
|
onStart() {
|
||
|
global.gc(); // when benchmark starts
|
||
|
},
|
||
|
});
|
||
|
|
||
|
benchmark.run({async: false});
|
||
|
|
||
|
return benchmark.stats;
|
||
|
};
|
||
|
|
||
|
const writeHeading2 = () => {
|
||
|
console.log('## Benchmark time for `diff-sequences` versus `diff`\n');
|
||
|
console.log('A ratio less than 1.0 means `diff-sequences` is faster.');
|
||
|
};
|
||
|
|
||
|
const writeHeading3 = n => {
|
||
|
console.log(`\n### n = ${n}\n`);
|
||
|
console.log('| name | % | ratio | improved | rme | baseline | rme |');
|
||
|
console.log('| :--- | ---: | :--- | :--- | ---: | :--- | ---: |');
|
||
|
};
|
||
|
|
||
|
const writeRow = (name, percent, statsImproved, statsBaseline) => {
|
||
|
const {mean: meanImproved, rme: rmeImproved} = statsImproved;
|
||
|
const {mean: meanBaseline, rme: rmeBaseline} = statsBaseline;
|
||
|
const ratio = meanImproved / meanBaseline;
|
||
|
|
||
|
console.log(
|
||
|
`| ${name} | ${percent}% | ${ratio.toFixed(
|
||
|
4,
|
||
|
)} | ${meanImproved.toExponential(4)} | ${rmeImproved.toFixed(
|
||
|
2,
|
||
|
)}% | ${meanBaseline.toExponential(4)} | ${rmeBaseline.toFixed(2)}% |`,
|
||
|
);
|
||
|
};
|
||
|
|
||
|
const testDeleteInsert = (tenths, more, less) => {
|
||
|
// For improved `diff-sequences` package, delete is often slower than insert.
|
||
|
const statsDeleteImproved = testImproved(more, less);
|
||
|
const statsDeleteBaseline = testBaseline(more, less);
|
||
|
writeRow('delete', tenths * 10, statsDeleteImproved, statsDeleteBaseline);
|
||
|
|
||
|
// For baseline `diff` package, many insertions is serious perf problem.
|
||
|
// However, the benchmark package cannot accurately measure for large n.
|
||
|
const statsInsertBaseline = testBaseline(less, more);
|
||
|
const statsInsertImproved = testImproved(less, more);
|
||
|
writeRow('insert', tenths * 10, statsInsertImproved, statsInsertBaseline);
|
||
|
};
|
||
|
|
||
|
const testChange = (tenths, expected, received) => {
|
||
|
const statsImproved = testImproved(expected, received);
|
||
|
const statsBaseline = testBaseline(expected, received);
|
||
|
writeRow('change', tenths * 10, statsImproved, statsBaseline);
|
||
|
};
|
||
|
|
||
|
const getItems = (n, callback) => {
|
||
|
const items = [];
|
||
|
|
||
|
for (let i = 0; i !== n; i += 1) {
|
||
|
const item = callback(i);
|
||
|
if (typeof item === 'string') {
|
||
|
items.push(item);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return items.join('\n');
|
||
|
};
|
||
|
|
||
|
// Simulate change of property name which is usually not same line.
|
||
|
// Expected: 0 1 2 3 4 5 6 7 8 9 and so on
|
||
|
// Received: 1 2 3 4 x0 5 6 7 8 9 and so on
|
||
|
const change2 = i => {
|
||
|
const j = i % 10;
|
||
|
return j === 4 ? `x${i - 4}` : j < 4 ? `${i + 1}` : `${i}`;
|
||
|
};
|
||
|
|
||
|
const testLength = n => {
|
||
|
const all = getItems(n, i => `${i}`);
|
||
|
|
||
|
writeHeading3(n);
|
||
|
|
||
|
[2, 4, 8].forEach(tenth => {
|
||
|
testDeleteInsert(tenth, all, getItems(n, i => i % 10 >= tenth && `${i}`));
|
||
|
});
|
||
|
testChange(1, all, getItems(n, i => (i % 10 === 0 ? `x${i}` : `${i}`)));
|
||
|
testChange(2, all, getItems(n, change2));
|
||
|
testChange(5, all, getItems(n, i => (i % 2 === 0 ? `x${i}` : `${i}`)));
|
||
|
testChange(10, all, getItems(n, i => `x${i}`)); // simulate TDD
|
||
|
};
|
||
|
|
||
|
writeHeading2();
|
||
|
|
||
|
testLength(20);
|
||
|
testLength(200);
|
||
|
testLength(2000);
|