我無法解釋爲什麼我的性能測試在2種不同類型的運行中返回顯着不同的結果。process.hrtime()的執行時間返回完全不同的結果
步驟來重現問題:
- 從要點獲取代碼: https://gist.github.com/AVAVT/83685bfe5280efc7278465f90657b9ea
- 運行
node practice1.generator
- 運行
node practice1.performance-test
practice1.generator
應該產生一個test-data.json
文件,並登錄一些搜索算法執行時間放入控制檯。 之後,practice1.performance-test
從test-data.json
中讀取,並對相同的數據執行完全相同的評估函數。
我的機器上輸出始終與此類似:
> node practice1.generator
Generate time: 9,307,061,368 nanoseconds
Total time using indexOf : 7,005,750 nanoseconds
Total time using for loop : 7,463,967 nanoseconds
Total time using binary search : 1,741,822 nanoseconds
Total time using interpolation search: 915,532 nanoseconds
> node practice1.performance-test
Total time using indexOf : 11,574,993 nanoseconds
Total time using for loop : 8,765,902 nanoseconds
Total time using binary search : 2,365,598 nanoseconds
Total time using interpolation search: 771,005 nanoseconds
注意在indexOf
和binary search
相對於其他算法的情況下,在執行時間上的差異。
如果我反覆運行node practice1.generator
或node practice1.performance-test
,但結果相當一致。
現在,這是如此令人不安,我找不到一種方法來找出哪個結果是可信的,以及爲什麼會出現這種差異。它是由生成的測試數組與JSON.parse-d測試數組之間的差異引起的嗎?或者是由process.hrtime()
造成的;還是我不明白的原因?
更新:我已經查明indexOf
箱子原因是因爲JSON.parse
。在practice1.generator
內部,tests
數組是原始生成的數組;而在practice1.performance-test
該數組是從json文件中讀取的,並且可能與原始數組有所不同。
如果內practice1.generator
我不是JSON.parse()
從字符串的新數組:
var tests2 = JSON.parse(JSON.stringify(tests));
performanceUtil.performanceTest(tests2);
的indexOf
的執行時間是現在這兩個文件是一致的。
> node practice1.generator
Generate time: 9,026,080,466 nanoseconds
Total time using indexOf : 11,016,420 nanoseconds
Total time using for loop : 8,534,540 nanoseconds
Total time using binary search : 1,586,780 nanoseconds
Total time using interpolation search: 742,460 nanoseconds
> node practice1.performance-test
Total time using indexOf : 11,423,556 nanoseconds
Total time using for loop : 8,509,602 nanoseconds
Total time using binary search : 2,303,099 nanoseconds
Total time using interpolation search: 718,723 nanoseconds
所以,至少我知道indexOf
運行原始陣列上,更好地差了JSON.parse
-d陣列上。 但我只知道原因,不知道爲什麼。
二進制搜索執行時間保持在2檔不同,始終如一地服用〜1.7ms在practice1.generator
(使用JSON.parse
-d對象即使)和〜2.3ms在practice1.performance-test
。
以下是與要點相同的代碼,供將來參考。
/*
* performance-utils.js
*/
'use strict';
const performanceTest = function(tests){
var tindexOf = process.hrtime();
tests.forEach(testcase => {
var result = testcase.input.indexOf(testcase.target);
if(result !== testcase.output) console.log("Errr", result, testcase.output);
});
tindexOf = process.hrtime(tindexOf);
var tmanual = process.hrtime();
tests.forEach(testcase => {
const arrLen = testcase.input.length;
var result = -1;
for(var i=0;i<arrLen;i++){
if(testcase.input[i] === testcase.target){
result = i;
break;
}
}
if(result !== testcase.output) console.log("Errr", result, testcase.output);
});
tmanual = process.hrtime(tmanual);
var tbinary = process.hrtime();
tests.forEach(testcase => {
var max = testcase.input.length-1;
var min = 0;
var check, num;
var result = -1;
while(max => min){
check = Math.floor((max+min)/2);
num = testcase.input[check];
if(num === testcase.target){
result = check;
break;
}
else if(num > testcase.target) max = check-1;
else min = check+1;
}
if(result !== testcase.output) console.log("Errr", result, testcase.output);
});
tbinary = process.hrtime(tbinary);
var tinterpolation = process.hrtime();
tests.forEach(testcase => {
var max = testcase.input.length-1;
var min = 0;
var result = -1;
var check, num;
while(max > min && testcase.target >= testcase.input[min] && testcase.target <= testcase.input[max]){
check = min + Math.round((max-min) * (testcase.target - testcase.input[min])/(testcase.input[max]-testcase.input[min]));
num = testcase.input[check];
if(num === testcase.target){
result = check;
break;
}
else if(testcase.target > num) min = check + 1;
else max = check - 1;
}
if(result === -1 && testcase.input[max] == testcase.target) result = max;
if(result !== testcase.output) console.log("Errr", result, testcase.output);
});
tinterpolation = process.hrtime(tinterpolation);
console.log(`Total time using indexOf : ${(tindexOf[0] * 1e9 + tindexOf[1]).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")} nanoseconds`);
console.log(`Total time using for loop : ${(tmanual[0] * 1e9 + tmanual[1]).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")} nanoseconds`);
console.log(`Total time using binary search : ${(tbinary[0] * 1e9 + tbinary[1]).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")} nanoseconds`);
console.log(`Total time using interpolation search: ${(tinterpolation[0] * 1e9 + tinterpolation[1]).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")} nanoseconds`);
}
module.exports = { performanceTest }
/*
* practice1.generator.js
*/
'use strict';
require('util');
const performanceUtil = require('./performance-utils');
const fs = require('fs');
const path = require('path');
const outputFilePath = path.join(__dirname, process.argv[3] || 'test-data.json');
const AMOUNT_TO_GENERATE = parseInt(process.argv[2] || 1000);
// Make sure ARRAY_LENGTH_MAX < (MAX_NUMBER - MIN_NUMBER)
const ARRAY_LENGTH_MIN = 10000;
const ARRAY_LENGTH_MAX = 18000;
const MIN_NUMBER = -10000;
const MAX_NUMBER = 10000;
const candidates = Array.from(Array(MAX_NUMBER - MIN_NUMBER + 1), (item, index) => MIN_NUMBER + index);
function createNewTestcase(){
var input = candidates.slice();
const lengthToGenerate = Math.floor(Math.random()*(ARRAY_LENGTH_MAX - ARRAY_LENGTH_MIN + 1)) + ARRAY_LENGTH_MIN;
while(input.length > lengthToGenerate){
input.splice(Math.floor(Math.random()*input.length), 1);
}
const notfound = input.length === lengthToGenerate ?
input.splice(Math.floor(Math.random()*input.length), 1)[0] : MIN_NUMBER-1;
const output = Math.floor(Math.random()*(input.length+1)) - 1;
const target = output === -1 ? notfound : input[output];
return {
input,
target,
output
};
}
var tgen = process.hrtime();
var tests = [];
while(tests.length < AMOUNT_TO_GENERATE){
tests.push(createNewTestcase());
}
fs.writeFileSync(outputFilePath, JSON.stringify(tests));
var tgen = process.hrtime(tgen);
console.log(`Generate time: ${(tgen[0] * 1e9 + tgen[1]).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")} nanoseconds`);
performanceUtil.performanceTest(tests);
/*
* practice1.performance-test.js
*/
'use strict';
require('util');
const performanceUtil = require('./performance-utils');
const fs = require('fs');
const path = require('path');
const outputFilePath = path.join(__dirname, process.argv[2] || 'test-data.json');
var tests = JSON.parse(fs.readFileSync(outputFilePath));
performanceUtil.performanceTest(tests);
您運行的是什麼版本的節點相當不錯的解釋,爲什麼在二進制JSON-解析方案獲勝,插值搜索? –
嗨@SamH。我正在使用node v6.11 – AVAVT
剛剛在8.5.0 Current上測試過並得到了相同的結果。 – AVAVT