Skip to content

Commit bd88a12

Browse files
authored
Merge pull request #181 from jpmorganchase/bench
Added new persistent, node.js benchmark suite
2 parents a79d565 + acf992f commit bd88a12

File tree

10 files changed

+455
-336
lines changed

10 files changed

+455
-336
lines changed

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
},
66
"scripts": {
77
"build": "[[ -z \"${PSP_DOCKER}\" ]] && npm run _build || npm run _emsdk -- npm run _build",
8+
"build_bench": "lerna run build_bench --stream",
9+
"bench": "lerna run bench --stream",
810
"build_test": "[[ -z \"${PSP_DOCKER}\" ]] && npm run _build_test || npm run _emsdk -- npm run _build_test",
911
"test": "npm run _test_perspective && npm run _test_viewer && npm run _test_hypergrid && npm run _test_highcharts",
1012
"quiet_test": "npm run _puppeteer -- npm run _quiet_test",

packages/perspective/test/config/benchmark.config.js renamed to packages/perspective/bench/config/benchmark.config.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ const path = require('path');
22
const common = require('../../src/config/common.config.js');
33

44
module.exports = Object.assign({}, common(), {
5-
plugins: [],
6-
entry: './test/js/benchmark.js',
5+
entry: './bench/js/benchmark.js',
6+
plugins: [],
7+
target: "node",
78
output: {
89
filename: 'benchmark.js',
910
path: path.resolve(__dirname, '../../build')
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
const path = require('path');
2+
const common = require('../../src/config/common.config.js');
3+
4+
module.exports = Object.assign({}, common(), {
5+
entry: './bench/js/report.js',
6+
target: "node",
7+
output: {
8+
filename: 'report.js',
9+
path: path.resolve(__dirname, '../../build')
10+
}
11+
});
File renamed without changes.
Lines changed: 295 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,295 @@
1+
/******************************************************************************
2+
*
3+
* Copyright (c) 2017, the Perspective Authors.
4+
*
5+
* This file is part of the Perspective library, distributed under the terms of
6+
* the Apache License 2.0. The full license can be found in the LICENSE file.
7+
*
8+
*/
9+
10+
import perspective from "../../src/js/perspective.node.js";
11+
const { performance } = require('perf_hooks');
12+
const fs = require("fs");
13+
const process = require('process');
14+
const os = require('os');
15+
16+
const FACTOR = 4;
17+
18+
import {histogram} from "d3-array";
19+
20+
String.prototype.hashCode = function(){
21+
var hash = 0;
22+
if (this.length == 0) return hash;
23+
for (let i = 0; i < this.length; i++) {
24+
let char = this.charCodeAt(i);
25+
hash = ((hash<<5)-hash)+char;
26+
hash = hash & hash; // Convert to 32bit integer
27+
}
28+
return "" + hash;
29+
}
30+
31+
function numberWithCommas(x) {
32+
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
33+
}
34+
35+
function print(message, append) {
36+
console.log(message);
37+
}
38+
39+
40+
function get_csv(url, callback) {
41+
callback(fs.readFileSync(url).toString());
42+
}
43+
44+
var __tests__ = [];
45+
var attempt = 1;
46+
47+
function test(iterations, f) {
48+
__tests__.push([iterations, f]);
49+
}
50+
51+
function run_tests() {
52+
print("Performance suite initiated.");
53+
print(os.platform() + " " + os.release());
54+
print(os.cpus()[0].model);
55+
print(__tests__.length + " scenarios found.");
56+
setTimeout(_run_tests);
57+
}
58+
59+
const RESULTS = [];
60+
61+
function stddev(values) {
62+
var avg = average(values);
63+
64+
var squareDiffs = values.map(function(value){
65+
var diff = value - avg;
66+
var sqrDiff = diff * diff;
67+
return sqrDiff;
68+
});
69+
70+
var avgSquareDiff = average(squareDiffs);
71+
72+
var stdDev = Math.sqrt(avgSquareDiff);
73+
return stdDev;
74+
}
75+
76+
function average(data) {
77+
var sum = data.reduce(function(sum, value){
78+
return sum + value;
79+
}, 0);
80+
81+
var avg = sum / data.length;
82+
return avg;
83+
}
84+
85+
function stats(results) {
86+
const r_avg = average(results);
87+
const r_std = stddev(results);
88+
const old_avg = OLD.data[RESULTS.length].avg;
89+
const old_std = OLD.data[RESULTS.length].std;
90+
let r_avg_diff = ((r_avg - old_avg) / r_avg) * 100;
91+
let r_std_diff = ((r_std - old_std) / r_std) * 100;
92+
r_avg_diff = (r_avg_diff < 0 ? "" : "+") + r_avg_diff.toFixed(2) + "%";
93+
r_std_diff = (r_std_diff < 0 ? "" : "+") + r_std_diff.toFixed(2) + "%";
94+
return {r_avg, r_std, r_avg_diff, r_std_diff, old_avg, old_std};
95+
}
96+
97+
let HTML = "<html><head><script>window.__RESULTS__=RESULTS1;window.__OLD__=OLD1</script><script>SCRIPT</script></head><body></body></html>";
98+
99+
const OLD = JSON.parse(fs.readFileSync('bench/results/results.json'));
100+
101+
function _run_tests() {
102+
103+
if (__tests__.length === 0) {
104+
print("Performance suite complete.");
105+
const data = JSON.stringify({
106+
data: RESULTS.map(y => {
107+
let results = y.results;
108+
let {r_avg, r_std, r_avg_diff, r_std_diff, old_avg, old_std} = y.stats;
109+
return {
110+
code: y.code,
111+
bins: histogram().thresholds(100)(results).map(x => ([x.x0, x.length])),
112+
avg: r_avg,
113+
std: r_std,
114+
avg_diff: r_avg_diff,
115+
std_diff: r_std_diff,
116+
old_avg: old_avg,
117+
old_std: old_std
118+
};
119+
}),
120+
model: os.cpus()[0].model,
121+
release: os.release(),
122+
platform: os.platform()
123+
});
124+
fs.writeFileSync('build/report.html', HTML.replace("SCRIPT", fs.readFileSync('build/report.js')).replace("RESULTS1", data).replace("OLD1", JSON.stringify(OLD)));
125+
fs.writeFileSync('bench/results/results.json', data);
126+
return;
127+
}
128+
129+
var f = __tests__[0];
130+
let iterations = f[0];
131+
f = f[1];
132+
__tests__ = __tests__.slice(1);
133+
print("Running scenario " + attempt + (__tests__.length > 0 ? (" (" + __tests__.length + " remaining).") : ""));
134+
attempt ++;
135+
136+
print(indent(code(f)));
137+
138+
var x = 0;
139+
var results = [];
140+
var start_all = performance.now();
141+
var testcase = function() {
142+
var start = performance.now();
143+
new Promise(f).then(function () {
144+
results.push(performance.now() - start);
145+
try {
146+
process.stdout.clearLine(); // clear current text
147+
process.stdout.cursorTo(0);
148+
process.stdout.write(x + "/" + iterations, false);
149+
} catch (e) {
150+
151+
}
152+
if (x === iterations) {
153+
print("");
154+
print("");
155+
print("Completed in " + numberWithCommas((performance.now() - start_all).toFixed(3)) + "ms", false);
156+
const r_stats = stats(results);
157+
RESULTS.push({results: results, code: code(f), stats: r_stats});
158+
let {r_avg, r_std, r_avg_diff, r_std_diff, old_avg, old_std} = r_stats;
159+
print(`avg: ${r_avg.toFixed(2)} (${old_avg.toFixed(2)}) ${r_avg_diff}`);
160+
print(`std: ${r_std.toFixed(2)} (${old_std.toFixed(2)}) ${r_std_diff}`);
161+
print("");
162+
setTimeout(_run_tests);
163+
} else {
164+
x++;
165+
setTimeout(testcase, 10);
166+
}
167+
});
168+
}
169+
setTimeout(testcase);
170+
171+
}
172+
173+
function code(f) {
174+
var y = f.toString().match(/function[^{]+\{([\s\S]*)\}$/)[1].replace(/\t/g, ' ');
175+
var indentation = y.split('\n')[1].match(/^[\s\t]*/)[0].length;
176+
var z = y.split('\n').map(function(q) { return q.slice(indentation, q.length); }).join('\n')
177+
return z;
178+
}
179+
180+
function indent(txt) {
181+
return txt.split('\n').map(x => " " + x).join('\n');
182+
}
183+
184+
185+
get_csv('build/flight_small.csv', function(csv) {
186+
187+
let table = perspective.table(csv);
188+
189+
test(50 * FACTOR, function(resolve) {
190+
perspective.table(csv);
191+
resolve();
192+
});
193+
194+
test(500 * FACTOR, function(resolve) {
195+
var view = table.view({
196+
filter: [["Origin", "contains", "P"]],
197+
aggregate: "count"
198+
});
199+
view.delete();
200+
resolve();
201+
});
202+
203+
test(500 * FACTOR, function(resolve) {
204+
var view = table.view({
205+
filter: [["Origin", "contains", "P"]],
206+
aggregate: "count"
207+
})
208+
view.to_json({
209+
end_row: 10
210+
}).then(function () {
211+
view.delete();
212+
resolve();
213+
});
214+
});
215+
216+
test(500 * FACTOR, function(resolve) {
217+
var view = table.view({
218+
filter: [["Origin", "contains", "P"]],
219+
row_pivot: ['Dest'],
220+
aggregate: "count"
221+
})
222+
view.to_json().then(function () {
223+
view.delete();
224+
resolve();
225+
});
226+
});
227+
228+
test(150 * FACTOR, function(resolve) {
229+
var view = table.view({
230+
filter: [["Origin", "contains", "P"]],
231+
aggregate: "count"
232+
})
233+
view.to_json().then(function () {
234+
view.delete();
235+
resolve();
236+
})
237+
});
238+
239+
test(150 * FACTOR, function(resolve) {
240+
var view = table.view({
241+
row_pivot: ['Dest'],
242+
aggregate: "count",
243+
row_pivot_depth: 1,
244+
})
245+
view.to_json().then(function() {
246+
view.delete();
247+
resolve();
248+
});
249+
});
250+
251+
// test(500, function(resolve) {
252+
// var table2 = table.add_computed([
253+
// {column: "Constant",
254+
// type: "number",
255+
// f: () => 0,
256+
// inputs: [],
257+
// }
258+
// ]);
259+
// table2.delete();
260+
// resolve();
261+
// });
262+
263+
// Arithmetic
264+
// test(500, function(resolve) {
265+
// var table2 = table.add_computed([
266+
// {column: "Speed",
267+
// type: "number",
268+
// f: (airtime, distance) => airtime/distance,
269+
// inputs: ["AirTime", "Distance"],
270+
// }
271+
// ]);
272+
// table2.delete();
273+
// resolve();
274+
// });
275+
276+
// // Generate string
277+
// test(500, function(resolve) {
278+
// var table2 = table.add_computed([
279+
// {column: "Day",
280+
// type: "string",
281+
// f: (x) => {
282+
// let days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thur', 'Fri', 'Sat'];
283+
// return days[x-1];
284+
// },
285+
// inputs: ["DayOfWeek"],
286+
// }
287+
// ]);
288+
// table2.delete();
289+
// resolve();
290+
// });
291+
292+
run_tests();
293+
});
294+
295+
//});

0 commit comments

Comments
 (0)