2017-09-18 8 views
12

मुझे यह समझने में परेशानी हो रही है कि क्यों मेरा प्रदर्शन परीक्षण 2 अलग-अलग प्रकार के रनों पर महत्वपूर्ण परिणाम देता है।process.hrtime() के साथ निष्पादन समय काफी अलग परिणाम

  1. सार से कोड प्राप्त करें::

    कदम समस्या को ठीक करने https://gist.github.com/AVAVT/83685bfe5280efc7278465f90657b9ea

  2. भागो node practice1.generator
  3. भागो node practice1.performance-test

practice1.generator एक test-data.json फाइल उत्पन्न करना चाहिए, और कुछ खोज लॉग इन करें कंसोल में एल्गोरिदम निष्पादन समय। उसके बाद, practice1.performance-testtest-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 में सरणी जेसन फ़ाइल से पढ़ी जाती है और शायद किसी भी तरह से मूल सरणी से अलग होती है।

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 सरणी पर मूल सरणी पर बेहतर और बदतर पता है। फिर भी मुझे केवल कारण पता है, कोई संकेत क्यों नहीं।

द्विआधारी खोज निष्पादन समय लगातार 1.7ms practice1.generator में (यहां तक ​​कि जब एक JSON.parse -d वस्तु का प्रयोग करके) 2.3ms practice1.performance-test में ले रही है ~ और ~ 2 फ़ाइलें पर अलग अलग रहते हैं,।


नीचे भविष्य के संदर्भ उद्देश्य के लिए प्रदान की गई जिंदगी के समान कोड है।

/* 
 
* 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);

+0

आप नोड के क्या संस्करण चला रहे हैं है? –

+0

हे वहाँ @ एसएएमएच। मैं नोड v6.11 – AVAVT

+0

का उपयोग कर रहा हूं बस 8.5.0 वर्तमान पर परीक्षण किया और एक ही परिणाम मिला। – AVAVT

उत्तर

2

आप पहले से ही देखा है के रूप में, प्रदर्शन अंतर तुलना की ओर जाता है: generated arrayJSON.parse बनाम डी। हमारे पास दोनों मामलों में क्या है: समान संख्या वाले समान सरणी? तो देखो प्रदर्शन प्रदर्शन वही होना चाहिए? सं

हर जावास्क्रिप्ट इंजन में एक ही मान का प्रतिनिधित्व करने के लिए विभिन्न डेटा प्रकार ढांचे (नंबर, ऑब्जेक्ट, अरै, आदि) है। ज्यादातर मामलों में अनुकूलक उपयोग करने के लिए सर्वोत्तम डेटा प्रकार का पता लगाने की कोशिश करता है। और अक्सर एरे के लिए hidden clases या tags जैसे कुछ अतिरिक्त मेटा जानकारी उत्पन्न करता है।

तो क्यों JSON.parse द्वारा बनाई सरणियों धीमी गति से कर रहे हैं:

डेटा प्रकार के बारे में कई बहुत अच्छा लेख हैं? पार्सर, मान बनाते समय, डेटा संरचनाओं को सही ढंग से अनुकूलित नहीं करता है, और परिणामस्वरूप हमें युगल के साथ untagged सरणी मिलती है। लेकिन हम बाद में Array.from के साथ सरणी को अनुकूलित कर सकते हैं और आपके मामले में जेनरेट किए गए सरणी के समान ही आपको smi एरे smi नंबरों के साथ प्राप्त हो सकते हैं। यहां आपके नमूने के आधार पर एक उदाहरण दिया गया है।

const fs = require('fs'); 
const path = require('path'); 
const outputFilePath = path.join(__dirname, process.argv[2] || 'test-data.json'); 

let tests = JSON.parse(fs.readFileSync(outputFilePath)); 

// for this demo we take only the first items array 
var arrSlow = tests[0].input; 
// `slice` copies array as-is 
var arrSlow2 = tests[0].input.slice(); 
// array is copied and optimized 
var arrFast = Array.from(tests[0].input); 

console.log(%HasFastSmiElements(arrFast), %HasFastSmiElements(arrSlow), %HasFastSmiElements(arrSlow2)); 
//> true, false, false 
console.log(%HasFastObjectElements(arrFast), %HasFastObjectElements(arrSlow), %HasFastObjectElements(arrSlow2)); 
//> false, true, true 
console.log(%HasFastDoubleElements(arrFast), %HasFastDoubleElements(arrSlow), %HasFastDoubleElements(arrSlow2)); 
//> false, false, false 

// small numbers and unboxed doubles in action 
console.log(%HasFastDoubleElements([Math.pow(2, 31)])); 
console.log(%HasFastSmiElements([Math.pow(2, 30)])); 

भागो यह node --allow-natives-syntax test.js

1

ठीक ... सब से पहले परीक्षण रणनीति के बारे में बात करने देता है साथ ...

चल रहा है कई बार परीक्षण प्रत्येक बिंदु के लिए एक बहुत अस्थिर अविश्वसनीय अलग परिणाम देता है। .. देखने के लिए यहाँ परिणाम

https://docs.google.com/spreadsheets/d/1Z95GtT85BljpNda4l-usPjNTA5lJtUmmcY7BVB8fFGQ/edit?usp=sharing

परीक्षण अद्यतन के बाद (100 परीक्षण चलाने के एक पंक्ति में और कैलोरी औसत culating) मैं स्कोर कि निष्पादन समय में मुख्य अंतर हैं:

  • indexOf और छोरों के लिए जनरेटर परिदृश्य में बेहतर काम कर रहे हैं
  • द्विआधारी खोज और प्रक्षेप खोज JSON-पार्स परिदृश्य
  • में बेहतर काम कर रहे हैं

कृपया से पहले गूगल दस्तावेज़ देख ...

ठीक .. महान ... यह बात भी बहुत कुछ समझाने के लिए आसान है ...मूल रूप से हम इस स्थिति में फंस जब यादृच्छिक स्मृति पहुँच (बाइनरी, प्रक्षेप खोज) और लगातार स्मृति पहुँच (indexOf, के लिए) अलग परिणाम


हममम दे। NodeJS

की स्मृति प्रबंधन मॉडल

सभी NodeJS पहले कई सरणी निरूपण है, मैं वास्तव में पता अंदर गहराई में जाने की सुविधा देता है केवल दो - numberArray, objectArray

चलें (सरणी कि किसी भी प्रकार की मूल्य शामिल कर सकते हैं इसका मतलब है) जनरेटर परिदृश्य पर देखो:

प्रारंभिक सरणी निर्माण NodeJS दौरान सक्षम पता लगाने के लिए कि आपके सरणी केवल संख्या शामिल सरणी के रूप में, प्रारंभ हो रहा है केवल संख्याओं से, और इसमें अन्य प्रकार के कुछ भी नहीं जोड़े गए हैं। यह सरल स्मृति आवंटन रणनीति, स्मृति में एक के बाद एक के लिए जा रहा पूर्णांकों का सिर्फ कच्चे पंक्ति का उपयोग कर में परिणाम है ...

सरणी स्मृति में array of raw numbers के रूप में प्रस्तुत किया जाता है, सबसे अधिक संभावना केवल memory paging table एक प्रभाव यहाँ

स्पष्ट रूप से इस तथ्य है बताते हैं कि CONSECUTIVE मेमोरी एक्सेस इस मामले में बेहतर काम करता है।

JSON-पार्स परिदृश्य को देखने की सुविधा देता है:

JSON के JSON पार्स संरचना के दौरान अप्रत्याशित है (NodeJS एक धारा JSON पार्सर (99.99% आत्मविश्वास) का उपयोग करता है), हर मूल्य सबसे आरामदायक के रूप में tracted है JSON को पार्स करने, इसलिए ...

सरणी स्मृति में array of references to the numbers के रूप में सिर्फ इसलिए जबकि JSON को पार्स इस समाधान ज्यादातर मामलों में अधिक उत्पादक है (और किसी को परवाह नहीं (शैतान))

जहाँ तक हम allo के रूप में प्रस्तुत किया जाता है, छोटे-छोटे टुकड़ों स्मृति से ढेर में cating स्मृति अधिक तरल पदार्थ रास्ता

इस मॉडल यादृच्छिक स्मृति पहुँच बेहतर परिणाम देता है में

इसके अलावा में भर जाता है, क्योंकि NodeJS इंजन कोई विकल्प हैं - उपयोग समय यह अच्छा बनाता है अनुकूलन करने के लिए या तो prefix tree या तो hash map जो यादृच्छिक स्मृति उपयोग में निरंतर पहुंच समय देता है परिदृश्यों

और यह काफी अच्छा स्पष्टीकरण क्यों JSON-पार्स परिदृश्य द्विआधारी दौरान जीतता है, प्रक्षेप खोज

+0

आपके उत्तर के लिए बहुत बहुत धन्यवाद। मैंने दोनों उत्तरों से बहुत कुछ सीखा, लेकिन चूंकि मेरा बक्षीस अंत के करीब है, इसलिए मुझे दस बिट्स चुनना होगा क्योंकि यह विशेष रूप से वी 8 पर कोड और आलेखों के रूप में सबूत प्रदान करता है। – AVAVT

+1

ठीक है ... बस ... उसने आपको एक और प्रश्न पर उत्तर दिया ... जवाब देने के लिए मेरे लिए सबसे कठिन सवाल यह बताने के लिए था कि आपको केवल "बेईमानी लाभ" का सामना क्यों किया गया था ... वैसे भी सवाल पूछने के लिए धन्यवाद, मैं इस पर सोचने में बहुत अच्छा समय था! –

संबंधित मुद्दे