2014-07-20 6 views
15

टाइप किए गए आरे सामान्य सामान्य सरणी क्यों नहीं होते हैं? मैं CLZ के लिए precalc मानों का उपयोग करना चाहता हूं (गणना शून्य शून्य समारोह)। और मैं नहीं चाहता कि वे सामान्य वस्तुओं के रूप में व्याख्या कर रहे हों?जावास्क्रिप्ट टाइपेडएरे प्रदर्शन

http://jsperf.com/array-access-speed-2/2

तैयारी कोड:

Benchmark.prototype.setup = function() { 
    var buffer = new ArrayBuffer(0x10000); 
    var Uint32 = new Uint32Array(buffer); 
    var arr = []; 
    for(var i = 0; i < 0x10000; ++i) { 
    Uint32[i] = (Math.random() * 0x100000000) | 0; 
    arr[i] = Uint32[i]; 
    } 
    var sum = 0; 
}; 

टेस्ट 1:

sum = arr[(Math.random() * 0x10000) | 0]; 

टेस्ट 2:

sum = Uint32[(Math.random() * 0x10000) | 0]; 

enter image description here

पीएस हो सकता है कि मेरे परफ परीक्षण अमान्य हैं मुझे सही करने के लिए स्वतंत्र महसूस करें।

+0

क्या आपने jsperf परीक्षण हटा दिए थे? मैं उन्हें और अधिक नहीं देख सकता – Bergi

+0

नहीं, मैंने इसे हटा नहीं दिया है। यह अजीब है। –

+1

बहुत, बहुत अजीब है कि वे इस तरह गायब हो गए होंगे! उनमें से सभी ... [इस एफएक्यू एंट्री] के प्रकाश में विशेष रूप से अजीब] (http://jsperf.com/faq#test- उपलब्धता)। मैंने यूआरएल को बदल दिया या कुछ, और ... कुछ भी नहीं खोजा है। मैं सरणी प्रदर्शन के अन्य परीक्षण पा सकता हूं, लेकिन सुखानोव और मेरा नहीं। मैंने एक [इसके लिए मुद्दा] उठाया है (https://github.com/mathiasbynens/jsperf.com/issues/197)। बेशक, अगर हम में से अधिक [jsPerf को दान किया गया है] (http://jsperf.com/faq#donate) (और मैंने अभी किया ... पहली बार * बतख सिर *) के लिए शायद यह चोट नहीं पहुंचाएगा। –

उत्तर

27
var buffer = new ArrayBuffer(0x10000); 
var Uint32 = new Uint32Array(buffer); 
खो दिया लंबाई पैरामीटर की वजह से लेकिन:

var Uint32 = new Uint32Array(0x10000); 

नई ArrayBuffer की वजह से नहीं (दोनों ही मामलों में Uint32.buffer देखना आप हमेशा एक सरणी बफर मिलती है:):

एक ही बात के रूप में नहीं है ArrayBuffer साथ आप प्रति तत्व 1 बाइट, Uint32Array के साथ आपके पास प्रति तत्व 4 बाइट हैं।

तो, पहले मामले में (और आपके कोड में), Uint32.length = 0x1000/4 और आपके लूप 4 गुना में 3 से बाहर हैं। लेकिन दुख की बात है कि आपको कभी भी त्रुटियां नहीं मिलेंगी, केवल खराब प्रदर्शन।

'नए ArrayBuffer' का उपयोग करना, आप इस तरह uint32 घोषित करने के लिए है:

var buffer = new ArrayBuffer(0x10000 * 4); 
var Uint32 = new Uint32Array(buffer); 

jsperf with (0x10000) और jsperf with (0x10000 * 4) देखें।

+3

यह एक शर्म की बात है कि लोग सामग्री के आकार पर आधारित उत्तरों को स्वीकार करते हैं, लेकिन सहीता पर नहीं। – MorrisLiang

+1

@ मॉरिसलिआंग, मैंने यही सोचा, लेकिन तब मुझे एहसास हुआ कि इस जवाब के बाद आधे साल में यह जवाब पोस्ट किया गया था। – user2033427

+0

@ मॉरिसलिआंग: ध्यान दें कि मेरे उत्तर * हमेशा * सेब के साथ सेब की तुलना करने के लिए 'नया Uint32Array (0x10000)' उपयोग किया जाता है, और अभी भी गति अंतर प्रकट होता है। लेकिन मैंने रैडोक के ऊपर जिस तरह से अंतर किया था, उसमें अंतर नहीं बुलाया। –

25

आधुनिक इंजन पर्दे के पीछे सच सरणियों का उपयोग भले ही आप Array का उपयोग करता है, तो उन्हें लगता है कि वे कर सकते हैं, संपत्ति नक्शा "सरणियों" पर वापस गिरने अगर आप कुछ उन्हें लगता है कि वे एक सच्चे सरणी उपयोग नहीं कर सकते है कि क्या करेंगे।

भी ध्यान रखें कि radsoc points out के रूप में, var buffer = new ArrayBuffer(0x10000) तो var Uint32 = new Uint32Array(buffer) एक uint32 सरणी जिसका आकार 0x4000 (0x10000/4), नहीं 0x10000 है, पैदा करता है क्योंकि मूल्य आप ArrayBuffer देना बाइट्स में है, लेकिन निश्चित रूप से Uint32Array प्रविष्टि प्रति चार बाइट्स देखते हैं । सेब के साथ सेब की तुलना करने के लिए नीचे दिए गए सभी new Uint32Array(0x10000)(और हमेशा इस संपादन से पहले भी) का उपयोग करते हैं।

तो new Uint32Array(0x10000) साथ, आइए वहीं से शुरू: http://jsperf.com/array-access-speed-2/11(दुर्भाग्य से, JSPerf इस परीक्षण और उसके परिणामों को खो दिया है, और ऑफलाइन पूरी तरह से अब है)

graph showing roughly equivalent performance

पता चलता है कि ऐसा इसलिए है क्योंकि आप ' सरणी को एक साधारण, अनुमानित तरीके से भरने के बाद, एक आधुनिक इंजन आगे बढ़ने के बजाए कवर के तहत एक वास्तविक सरणी (उसके प्रदर्शन लाभ के साथ) का उपयोग जारी रखता है। हम मूल रूप से दोनों के लिए एक ही प्रदर्शन देखते हैं। गति में अंतर Uint32 मान लेने वाले रूपांतरण प्रकार से संबंधित हो सकता है और इसे sum पर number के रूप में असाइन कर सकता है (हालांकि मुझे आश्चर्य है कि उस प्रकार का रूपांतरण स्थगित नहीं हुआ है ...)।

, कुछ अराजकता जोड़े हालांकि: ", सरणियों" ...

var Uint32 = new Uint32Array(0x10000); 
var arr = []; 
for (var i = 0x10000 - 1; i >= 0; --i) { 
    Uint32[Math.random() * 0x10000 | 0] = (Math.random() * 0x100000000) | 0; 
    arr[Math.random() * 0x10000 | 0] = (Math.random() * 0x100000000) | 0; 
} 
var sum = 0; 

ताकि इंजन पुराने जमाने संपत्ति के नक्शे पर पीछे हटना है और आप देखते हैं कि टाइप सरणियों स्पष्ट रूप से मात पुराने ढंग का तरह: http://jsperf.com/array-access-speed-2/3(दुर्भाग्य से, JSPerf खो दिया है इस परीक्षण और उसके परिणाम)

bar graph showing marked performance improvement for typed arrays

चालाक, इन JavaSc रिप्ट इंजन इंजीनियरों ...

Array सरणी की गैर-सरणी प्रकृति के साथ आप जो विशिष्ट चीज करते हैं, हालांकि; पर विचार करें:

var Uint32 = new Uint32Array(0x10000); 
var arr = []; 
arr.foo = "bar";       // <== Non-element property 
for (var i = 0; i < 0x10000; ++i) { 
    Uint32[i] = (Math.random() * 0x100000000) | 0; 
    arr[i] = (Math.random() * 0x100000000) | 0; 
} 
var sum = 0; 

कि अभी भी सरणी जाहिर भरने है, लेकिन हम यह करने के लिए एक गैर-तत्व संपत्ति (foo) जोड़ें। http://jsperf.com/array-access-speed-2/4(दुर्भाग्य से, JSPerf इस परीक्षण और उसके परिणामों को खो दिया है) जाहिर है, इंजन काफी चतुर हैं, और जब तत्व संपत्तियों के लिए एक सच्चे सरणी का उपयोग जारी रखने तरफ है कि गैर तत्व संपत्ति रखें:

bar graph showing performance improvement for standard arrays when <code>Array</code> array gets non-element property

मैं एक नुकसान की व्याख्या करने का एक सा पर हूँ क्यों मानक सरणियों मिलना चाहिए तेजी वहाँ ऊपर हमारा पहला परीक्षण की तुलना में। माप त्रुटि? Math.random में वैगरीज? लेकिन हम अभी भी बहुत यकीन कर रहे हैं कि Array में सरणी-विशिष्ट डेटा अभी भी एक वास्तविक सरणी है।

जबकि यदि हम एक ही काम करते हैं लेकिन उलटे क्रम में भरने:

var Uint32 = new Uint32Array(0x10000); 
var arr = []; 
arr.foo = "bar";       // <== Non-element property 
for (var i = 0x10000 - 1; i >= 0; --i) { // <== Reverse order 
    Uint32[i] = (Math.random() * 0x100000000) | 0; 
    arr[i] = (Math.random() * 0x100000000) | 0; 
} 
var sum = 0; 

... हम वापस टाइप किया सरणियों को IE11 पर छोड़कर   — बाहर जीतने मिलती है: http://jsperf.com/array-access-speed-2/9(दुर्भाग्य से, JSPerf है इस परीक्षण और उसके परिणाम)

graph showing typed arrays winning except on IE11

+2

दिलचस्प, धन्यवाद। चीजें वास्तविक उपयोग के मामले में अधिक जटिल हो जाती हैं, जहां विभिन्न वस्तुओं/सरणी के निरंतर आवंटन/विघटन स्मृति विखंडन की ओर जाता है, और स्मृति पूर्व-आवंटित नहीं होने पर प्रसंस्करण समय विस्फोट होता है। वास्तविक जीवन में टाइप किए गए ऐरे को कम आवंटित किया जाएगा क्योंकि वे प्री-आवंटित हैं (आरक्यू: जेएस एरेज़ भी प्रीलोकेटेड हो सकते हैं ...) – GameAlchemist

+0

सुंदर स्पष्टीकरण, इसके लिए धन्यवाद। – qodeninja

+0

लिखने वाले सरणी के लिए पूर्व-कैल्स किए गए int32 मानों को लिखना तेज़ है। सरणी में छेद नहीं होते हैं (हाँ, छेद तेज़ होते हैं लेकिन ...)। सरणी preallocated है। इसलिए टाइपिंग सरणी बनाना और प्रारंभ करना तेज़ है। लेकिन जब आप Uint32Array से पढ़ने का प्रयास करते हैं तो मान को डबल में परिवर्तित किया जाना चाहिए (नीचे मेरा उत्तर देखें)। आपको Int32Array (x64 पर) या Int16Array (x86 पर) का उपयोग करना चाहिए (नीचे मेरा उत्तर देखें)। – vitaliydev

0

आपके मामले में खराब प्रदर्शन का कारण है कि आप सरणी लंबाई के साथ बग की वजह से Uint32Array का उपयोग करते समय सरणी के बाहर पढ़ने की कोशिश करते हैं।

लेकिन यह तो असली कारण नहीं हो सकता है अगर: Uint32Array के बजाय Int32Array उपयोग करने के लिए

प्रयास करें। मुझे लगता है कि वी 8 चर में uint32 प्रकार नहीं हो सकता है लेकिन int32/double/सूचक हो सकता है। तो जब आप वैरिएबल के लिए uint32 प्रकार असाइन करते हैं तो इसे धीमे डबल में परिवर्तित कर दिया जाएगा।

यदि आप वी 8 के 32-बिट संस्करण का उपयोग करते हैं तो चर के पास int31/double/सूचक प्रकार हो सकता है। तो int32 भी डबल में परिवर्तित हो जाएगा। लेकिन यदि आप सामान्य सरणी का उपयोग करते हैं और सभी मान int31 हैं तो रूपांतरण की आवश्यकता नहीं है इसलिए सामान्य सरणी तेज हो सकती है।

int16 का उपयोग करने के लिए int32 (साइन और किसी के पूरक के कारण) प्राप्त करने के लिए कुछ रूपांतरणों की आवश्यकता हो सकती है। Uint16 को रूपांतरण की आवश्यकता नहीं होगी क्योंकि V8 बाईं ओर शून्य जोड़ सकता है।

पीएस। आप दिलचस्पी पॉइंटर्स और int31 (या x64 पर int32) हो सकते हैं V8 में वही चीज़ें हैं। इसका मतलब यह भी है कि int32 को x64 पर 8 बाइट्स की आवश्यकता होगी। इसके अलावा यही कारण है कि x86 पर कोई int32 प्रकार नहीं है: क्योंकि अगर हम पूर्णांक को संग्रहीत करने के लिए सभी 32 बिट्स का उपयोग करेंगे तो हमारे पास पॉइंटर्स को बचाने के लिए कोई जगह नहीं होगी।

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