1 | var path = require('path');
|
2 | var async = require('async');
|
3 |
|
4 | var _ = require('lodash');
|
5 | try { var memwatch = require('memwatch'); } catch (ex) { }
|
6 | var rewire = require('rewire');
|
7 |
|
8 |
|
9 |
|
10 | var functions = {
|
11 | countFiles: 'countFiles',
|
12 | fibonacci: 'fibonacci',
|
13 | largest: 'largest'
|
14 |
|
15 | };
|
16 |
|
17 |
|
18 | var 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 |
|
31 |
|
32 | var SELECTED_FUNCTION = functions.largest;
|
33 |
|
34 | var SELECTED_VARIANT = variants.asyncawait;
|
35 |
|
36 | var SAMPLES_PER_RUN = 100;
|
37 |
|
38 | var RUNS_PER_BENCHMARK = 10;
|
39 |
|
40 | var CONCURRENCY_FACTOR = 10;
|
41 |
|
42 |
|
43 | var JUST_CHECK_THE_FUNCTION = false;
|
44 | var USE_SAME_SYMBOL_FOR_ALL_SAMPLES = true;
|
45 | var USE_MOCK_FS = false;
|
46 | var OUTPUT_GC_STATS = true;
|
47 | var OUTPUT_SAMPLES_PER_SEC_SUMMARY = false;
|
48 |
|
49 |
|
50 |
|
51 |
|
52 |
|
53 | var fullGCs = 0;
|
54 | var incrGCs = 0;
|
55 | var leaked = 0;
|
56 | if (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 |
|
70 | if (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 |
|
86 | function 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 |
|
151 | function 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 |
|
195 | function 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 | }
|