UNPKG

8.71 kBJavaScriptView Raw
1var path = require('path');
2var async = require('async'); // NB: async is used here in the benchmarking code, in case co or
3 // asyncawait won't run on the version of node being benchmarked.
4var _ = require('lodash');
5try { var memwatch = require('memwatch'); } catch (ex) { }
6var rewire = require('rewire');
7
8
9// Functions available for benchmarking.
10var functions = {
11 countFiles: 'countFiles',
12 fibonacci: 'fibonacci',
13 largest: 'largest'
14
15};
16
17// Variants available for benchmarking.
18var variants = {
19 async: 'async',
20 asyncawait: 'asyncawait',
21 asyncx: 'asyncx',
22 bluebird: 'bluebird',
23 callbacks: 'callbacks',
24 co: 'co',
25 synchronous: 'synchronous'
26};
27
28
29// ================================================================================
30// Benchmark configuration - adjust to suit.
31
32var SELECTED_FUNCTION = functions.largest;
33
34var SELECTED_VARIANT = variants.asyncawait;
35
36var SAMPLES_PER_RUN = 100; // How many times the function will be called per run.
37
38var RUNS_PER_BENCHMARK = 10; // How many runs make up the whole benchmark.
39
40var CONCURRENCY_FACTOR = 10; // Max number of concurrent invocations of the function.
41
42// Some additional switches
43var JUST_CHECK_THE_FUNCTION = false; // If true, just call the function once and display its results.
44var USE_SAME_SYMBOL_FOR_ALL_SAMPLES = true; // If true, all samples will use the same symbol ('.'). Otherwise, concurrent samples will use distinct symbols.
45var USE_MOCK_FS = false; // If true, uses a mocked 'fs' module returning fixed in-memory results.
46var OUTPUT_GC_STATS = true; // If true, indicate GC pauses and statistics, and indicate possible memory leaks.
47var OUTPUT_SAMPLES_PER_SEC_SUMMARY = false; // If true, print all samples/sec numbers at the end, to export for anaysis (eg for charting).
48
49// ================================================================================
50
51
52// Set up memory diagnostics
53var fullGCs = 0;
54var incrGCs = 0;
55var leaked = 0;
56if (OUTPUT_GC_STATS && memwatch) {
57 memwatch.on('leak', function(info) {
58 leaked += info.growth;
59 process.stdout.write(' [LEAK+' + info.growth +'] ');
60 });
61 memwatch.on('stats', function(stats) {
62 fullGCs = stats.num_full_gc;
63 incrGCs = stats.num_inc_gc;
64 process.stdout.write(' [GC] ');
65 });
66}
67
68
69// Run the benchmark (or just check the function).
70if (JUST_CHECK_THE_FUNCTION) {
71 var name = SELECTED_FUNCTION + '-' + SELECTED_VARIANT;
72 var sample = createSampleFunction();
73 console.log("========== CHECKING '" + name + "': ==========");
74 sample(function(err, result) {
75 console.log(err || result);
76 if (OUTPUT_GC_STATS) {
77 console.log("========== GCs: " + fullGCs + 'full/' + incrGCs + "incr ==========");
78 console.log("========== Leaked: " + leaked + " ==========");
79 }
80 });
81} else {
82 benchmark();
83}
84
85
86function benchmark() {
87 var name = SELECTED_FUNCTION + '-' + SELECTED_VARIANT;
88 var sample = createSampleFunction();
89 var allSamplesPerSec = [];
90 console.log('========== PERFORMING ' + RUNS_PER_BENCHMARK + " RUNS ON '" + name + "': ==========\n");
91 var times = [];
92 async.timesSeries(
93 RUNS_PER_BENCHMARK,
94 function (n, next) {
95 process.stdout.write('RUN ' + (n + 1));
96 run(sample, function (err, timing) {
97 if (err) {
98 next(err);
99 } else {
100 times.push(timing.totalElapsed);
101 allSamplesPerSec.push(SAMPLES_PER_RUN * 1000.0 / timing.totalElapsed);
102 var msg = SAMPLES_PER_RUN
103 + ' samples took '
104 + (timing.totalElapsed / 1000.0)
105 + ' seconds ('
106 + (SAMPLES_PER_RUN * 1000.0 / timing.totalElapsed)
107 + ' samples/sec), average latency per sample: '
108 + timing.perSample
109 + 'ms';
110 if (OUTPUT_GC_STATS) {
111 msg = msg
112 + ', GCs: '
113 + timing.fullGCs
114 + 'full/'
115 + timing.incrGCs
116 + 'incr, leaked: '
117 + timing.leaked;
118 }
119 console.log(msg + '\n');
120 next();
121 }
122 });
123 },
124 function (err) {
125 if (err) {
126 console.log(err);
127 process.exit();
128 } else {
129 totalTime = _.reduce(times, function (sum, time) { return sum + time; });
130 var averageTime = totalTime / RUNS_PER_BENCHMARK;
131 var msg = 'Average time: '
132 + (averageTime / 1000.0)
133 + ' seconds ('
134 + (SAMPLES_PER_RUN * 1000.0 / averageTime)
135 + ' samples/sec)';
136 console.log('========== ' + msg + ' ==========');
137 if (OUTPUT_GC_STATS) {
138 console.log("========== GCs: " + fullGCs + 'full/' + incrGCs + "incr ==========");
139 console.log("========== Leaked: " + leaked + " ==========");
140 }
141 if (OUTPUT_SAMPLES_PER_SEC_SUMMARY) {
142 console.log("========== Summary of samples/sec for all runs: ==========");
143 console.log(allSamplesPerSec.join(', '));
144 }
145 }
146 });
147}
148
149
150
151function run(sample, callback) {
152 var chars = USE_SAME_SYMBOL_FOR_ALL_SAMPLES ? '.' : './#$@%^&*+!=-?~`|()[]ABCDEFGHIJKLMNOPQRS';
153 var start = new Date().getTime();
154 var startFullGCs = fullGCs;
155 var startIncrGCs = incrGCs;
156 var startLeaked = leaked;
157 var sumOfTimePerSample = 0.0;
158 async.times(
159 CONCURRENCY_FACTOR,
160 function (m, nextOuter) {
161 var char = chars.charAt(m % chars.length);
162 async.timesSeries(
163 1.0 * SAMPLES_PER_RUN / CONCURRENCY_FACTOR,
164 function (n, nextInner) {
165 process.stdout.write(char);
166 var start = new Date().getTime();
167 sample(function() {
168 var end = new Date().getTime();
169 sumOfTimePerSample += (end - start);
170 nextInner();
171 });
172 },
173 function (err) {
174 nextOuter(err);
175 }
176 );
177 },
178 function(err, res) {
179 process.stdout.write('\n');
180 if (err) { callback(err); return; }
181 var perSample = sumOfTimePerSample / SAMPLES_PER_RUN;
182 var totalElapsed = new Date().getTime() - start;
183 callback(null, {
184 perSample: perSample,
185 totalElapsed: totalElapsed,
186 fullGCs: fullGCs - startFullGCs,
187 incrGCs: incrGCs - startIncrGCs,
188 leaked: leaked - startLeaked
189 });
190 }
191 );
192};
193
194
195function createSampleFunction() {
196 var moduleId = './' + SELECTED_FUNCTION + '/' + SELECTED_FUNCTION + '-' + SELECTED_VARIANT;
197 var selectedFunction = rewire(moduleId);
198 if (USE_MOCK_FS) selectedFunction.__set__('fs', require('./mockfs'));
199 switch (SELECTED_FUNCTION) {
200 case functions.countFiles:
201 var dirToCheck = path.join(__dirname, '.');
202 var sample = function (callback) {
203 selectedFunction(dirToCheck, function (err, result) {
204 setImmediate(callback, err, result);
205 });
206 };
207 break;
208
209 case functions.fibonacci:
210 var n = 5;
211 var sample = function (callback) {
212 selectedFunction(n, function (err, result) {
213 setImmediate(callback, err, result);
214 });
215 };
216 break;
217
218 case functions.largest:
219 var dirToCheck = path.join(__dirname, '.');
220 var options = { recurse: true, preview: true };
221 var sample = function (callback) {
222 selectedFunction(dirToCheck, options, function (err, result) {
223 setImmediate(callback, err, result);
224 });
225 };
226 break;
227
228 }
229 return sample;
230}