javascript - V8 में इस कोड स्निपेट का उपयोग करके<=धीमा क्यों है?




(5)

मैं V8 के साथ जावास्क्रिप्ट स्पीड लिमिट को तोड़ते हुए स्लाइड्स को पढ़ रहा हूं, और नीचे दिए गए कोड की तरह एक उदाहरण है। मैं यह पता नहीं लगा सकता कि <= इस मामले में धीमा है < , क्या कोई इसे समझा सकता है? किसी भी टिप्पणी की सराहना की है।

धीरे:

this.isPrimeDivisible = function(candidate) {
    for (var i = 1; i <= this.prime_count; ++i) {
        if (candidate % this.primes[i] == 0) return true;
    }
    return false;
} 

(संकेत: प्राइम लंबाई की एक सरणी है प्राइम_काउंट)

और तेज:

this.isPrimeDivisible = function(candidate) {
    for (var i = 1; i < this.prime_count; ++i) {
        if (candidate % this.primes[i] == 0) return true;
    }
    return false;
} 

[अधिक जानकारी] मेरे स्थानीय पर्यावरण परीक्षण में गति में सुधार महत्वपूर्ण है, परिणाम इस प्रकार हैं:

V8 version 7.3.0 (candidate) 

धीरे:

 time d8 prime.js
 287107
 12.71 user 
 0.05 system 
 0:12.84 elapsed 

और तेज:

time d8 prime.js
287107
1.82 user 
0.01 system 
0:01.84 elapsed

Answers

मैं Google पर V8 पर काम करता हूं, और मौजूदा उत्तरों और टिप्पणियों के शीर्ष पर कुछ अतिरिक्त जानकारी प्रदान करना चाहता था।

संदर्भ के लिए, यहाँ स्लाइड से पूर्ण कोड उदाहरण दिया गया है :

var iterations = 25000;

function Primes() {
  this.prime_count = 0;
  this.primes = new Array(iterations);
  this.getPrimeCount = function() { return this.prime_count; }
  this.getPrime = function(i) { return this.primes[i]; }
  this.addPrime = function(i) {
    this.primes[this.prime_count++] = i;
  }
  this.isPrimeDivisible = function(candidate) {
    for (var i = 1; i <= this.prime_count; ++i) {
      if ((candidate % this.primes[i]) == 0) return true;
    }
    return false;
  }
};

function main() {
  var p = new Primes();
  var c = 1;
  while (p.getPrimeCount() < iterations) {
    if (!p.isPrimeDivisible(c)) {
      p.addPrime(c);
    }
    c++;
  }
  console.log(p.getPrime(p.getPrimeCount() - 1));
}

main();

सबसे पहले और सबसे महत्वपूर्ण, प्रदर्शन अंतर का सीधा < और <= ऑपरेटरों से कोई लेना-देना नहीं है। तो कृपया अपने कोड में <= से बचने के लिए हुप्स के माध्यम से कूद न करें क्योंकि आप स्टैक ओवरफ्लो पर पढ़ते हैं कि यह धीमा है --- यह नहीं है!

दूसरा, लोगों ने बताया कि सरणी "छिद्रपूर्ण" है। यह ओपी के पोस्ट में कोड स्निपेट से स्पष्ट नहीं था, लेकिन जब आप इस कोड को देखते हैं तो यह स्पष्ट होता है।

this.primes = new Array(iterations);

यह V8 में HOLEY तत्वों के साथ एक सरणी में परिणाम देता है, भले ही सरणी पूरी तरह से भरा / पैक / सन्निहित हो। सामान्य तौर पर, छिद्रपूर्ण सरणियों पर संचालन पैक किए गए सरणियों पर संचालन की तुलना में धीमा होता है, लेकिन इस मामले में अंतर नगण्य है: यह 1 अतिरिक्त एसआईआई ( छोटे पूर्णांक ) चेक (छेदों के खिलाफ गार्ड) की मात्रा में होता है जब भी हम हिट this.primes[i] लूप के भीतर। isPrimeDivisible । कोई बड़ी बात नहीं!

TL, डॉ । सरणी HOLEY जा रहा है यहाँ समस्या नहीं है।

दूसरों ने बताया कि कोड सीमा से बाहर पढ़ता है। यह आम तौर पर सरणियों की लंबाई से परे पढ़ने से बचने की सिफारिश की जाती है, और इस मामले में यह वास्तव में प्रदर्शन में भारी गिरावट से बचा होगा। लेकिन हालांकि क्यों? V8 केवल मामूली प्रदर्शन प्रभाव के साथ इन आउट-ऑफ-बाउंड परिदृश्यों में से कुछ को संभाल सकता है। इस विशेष मामले में ऐसा क्या खास है, फिर?

आउट-ऑफ- this.primes[i] परिणाम में this.primes[i] इस लाइन पर undefined :

if ((candidate % this.primes[i]) == 0) return true;

और यह हमें वास्तविक मुद्दे पर लाता है: % ऑपरेटर का उपयोग अब गैर-पूर्णांक ऑपरेंड के साथ किया जा रहा है!

  • integer % someOtherInteger गणना बहुत कुशलता से की जा सकती है; जावास्क्रिप्ट इंजन इस मामले के लिए अत्यधिक अनुकूलित मशीन कोड का उत्पादन कर सकते हैं।

  • दूसरी ओर integer % undefined एक तरह से कम कुशल Float64Mod मात्रा पर Float64Mod , क्योंकि undefined को एक दोहरे के रूप में दर्शाया जाता है।

इस पंक्ति पर कोड स्निपेट को वास्तव में <= में बदलकर सुधार किया जा सकता है:

for (var i = 1; i <= this.prime_count; ++i) {

... इसलिए नहीं कि <= किसी भी तरह से एक बेहतर ऑपरेटर है < , लेकिन सिर्फ इसलिए कि इस विशेष मामले में पढ़े जाने वाले आउट-ऑफ-बाउंड्स से बचा जाता है।


इसमें कुछ वैज्ञानिकता जोड़ने के लिए, यहाँ एक jsperf है

https://jsperf.com/ints-values-in-out-of-array-bounds

यह एक सरणी के नियंत्रण मामले का परीक्षण करता है, जो कि ints से भरा होता है और सीमा के भीतर रहकर मॉड्यूलर अंकगणित करता है। इसके 5 परीक्षण मामले हैं:

  • 1. सीमा से बाहर लूपिंग
  • 2. होली सरण
  • 3. NaNs के खिलाफ मॉड्यूलर अंकगणित
  • 4. पूरी तरह से अपरिभाषित मूल्य
  • 5. एक new Array() का उपयोग करना new Array()

यह दर्शाता है कि प्रदर्शन के लिए पहले 4 मामले वास्तव में खराब हैं। सीमा से बाहर लूपिंग अन्य 3 की तुलना में थोड़ा बेहतर है, लेकिन सभी 4 सर्वश्रेष्ठ मामले की तुलना में लगभग 98% धीमी हैं।
new Array() मामला लगभग कच्चा सरणी जितना ही अच्छा है, बस कुछ प्रतिशत धीमा है।


अन्य उत्तरों और टिप्पणियों में उल्लेख किया गया है कि दो छोरों के बीच का अंतर यह है कि पहला एक दूसरे की तुलना में एक अधिक पुनरावृत्ति निष्पादित करता है। यह सच है, लेकिन एक सरणी में जो 25,000 तत्वों तक बढ़ती है, कम या ज्यादा एक पुनरावृत्ति केवल एक miniscule अंतर बनाती है। बॉलपार्क अनुमान के अनुसार, यदि हम औसत लंबाई मान लेते हैं कि यह 12,500 है, तो हम जो अंतर की उम्मीद कर सकते हैं वह लगभग 1 / 12,500, या केवल 0.008% होना चाहिए।

यहां प्रदर्शन का अंतर उस एक अतिरिक्त पुनरावृत्ति द्वारा समझाया जाएगा, और प्रस्तुति के अंत में समस्या को समझाया गया है।

this.primes एक सन्निहित सरणी है (प्रत्येक तत्व एक मान रखता है) और तत्व सभी संख्याएँ हैं।

जावास्क्रिप्ट इंजन ऐसे सरणी को वास्तविक संख्याओं का एक सरल सरणी होने के लिए अनुकूलित कर सकता है, वस्तुओं की एक सरणी के बजाय जो संख्याओं को समाहित करता है, लेकिन इसमें अन्य मान या कोई मान नहीं हो सकते हैं। पहला प्रारूप एक्सेस करने के लिए बहुत तेज़ है: यह कम कोड लेता है, और सरणी बहुत छोटा होता है इसलिए यह कैश में बेहतर होगा। लेकिन कुछ परिस्थितियां हैं जो इस अनुकूलित प्रारूप को उपयोग करने से रोक सकती हैं।

एक शर्त यह होगी कि कुछ एरे तत्व गायब हैं। उदाहरण के लिए:

let array = [];
a[0] = 10;
a[2] = 20;

अब a[1] का मूल्य क्या है? इसका कोई मूल्य नहीं है । (यह कहना भी सही नहीं है कि इसका undefined मूल्य है - undefined मान युक्त एक सरणी तत्व एक सरणी तत्व से अलग है जो पूरी तरह से गायब है।)

केवल संख्याओं के साथ इसका प्रतिनिधित्व करने का कोई तरीका नहीं है, इसलिए जावास्क्रिप्ट इंजन को कम अनुकूलित प्रारूप का उपयोग करने के लिए मजबूर किया जाता है। यदि a[1] में अन्य दो तत्वों की तरह एक संख्यात्मक मान होता है, तो सरणी को संभवतः संख्याओं की एक सरणी में अनुकूलित किया जा सकता है।

यदि आप प्रस्तुति में चर्चा की गई सरणी के सीमा के बाहर किसी तत्व तक पहुंचने का प्रयास करते हैं, तो व्यूह-निर्मित प्रारूप में मजबूर किए जाने के लिए एक और कारण हो सकता है।

पहले लूप के साथ <= सरणी के अंत में एक तत्व को पढ़ने का प्रयास करता है। एल्गोरिथ्म अभी भी सही ढंग से काम करता है, क्योंकि अंतिम अतिरिक्त पुनरावृत्ति में:

  • this.primes[i] undefined मूल्यांकन करता है क्योंकि i सरणी के अंत से अतीत i
  • candidate % undefined ( candidate % undefined के किसी भी मूल्य के लिए) NaN मूल्यांकन करता है।
  • NaN == 0 false मूल्यांकन करता false
  • इसलिए, return true निष्पादित नहीं किया गया है।

तो यह ऐसा है जैसे अतिरिक्त पुनरावृत्ति कभी नहीं हुई - इसका बाकी तर्क पर कोई प्रभाव नहीं है। कोड अतिरिक्त पुनरावृत्ति के बिना ही परिणाम उत्पन्न करता है।

लेकिन वहां पहुंचने के लिए, इसने सरणी के अंत में एक अचूक तत्व को पढ़ने की कोशिश की। यह सरणी को अनुकूलन से बाहर कर देता है - या कम से कम इस बात के समय।

< साथ दूसरा लूप केवल उन तत्वों को पढ़ता है जो सरणी के भीतर मौजूद हैं, इसलिए यह एक अनुकूलित सरणी और कोड की अनुमति देता है।

समस्या 90-91 के पन्नों में वर्णित है, इससे पहले और बाद के पृष्ठों में संबंधित चर्चा के साथ।

मैं इस Google I / O प्रस्तुति में शामिल हुआ और बाद में स्पीकर (V8 लेखकों में से एक) के साथ बात की। मैं अपने स्वयं के कोड में एक तकनीक का उपयोग कर रहा था जिसमें एक विशेष परिस्थिति को अनुकूलित करने के प्रयास के रूप में एक सरणी के अंत में एक गुमराह (हिंद में) पढ़ने की कोशिश शामिल थी। उन्होंने पुष्टि की कि यदि आप किसी सरणी के अंत में भी पढ़ने की कोशिश करते हैं , तो यह सरल अनुकूलित प्रारूप का उपयोग करने से रोक देगा।

यदि V8 लेखक ने जो कहा है वह अभी भी सत्य है, तो सरणी के अंत को पढ़ने से इसे अनुकूलित होने से रोका जा सकेगा और इसे धीमी प्रारूप में वापस आना होगा।

अब यह संभव है कि इस मामले को कुशलता से संभालने के लिए V8 में सुधार किया गया हो, या अन्य जावास्क्रिप्ट इंजन इसे अलग तरीके से संभालते हों। मैं उस पर एक तरह से या दूसरे तरीके से नहीं जानता, लेकिन यह विचलन है कि प्रस्तुति किस बारे में बात कर रही थी।


TL; DR धीमा लूप ऐरे 'आउट-ऑफ-बाउंड्स' तक पहुँचने के कारण है, जो या तो इंजन को कम या कोई अनुकूलन के साथ फ़ंक्शन को फिर से शुरू करने के लिए मजबूर करता है या किसी भी अनुकूलन के साथ फ़ंक्शन को संकलित नहीं करने के लिए शुरू होता है ( यदि (JIT-) कंपाइलर ने पहले संकलन 'संस्करण' से पहले इस स्थिति का पता लगाया / संदेह किया, तो नीचे क्यों पढ़ें;

किसी को सिर्फ यह कहना है (पूरी तरह से चकित किसी ने पहले से ही नहीं किया है):
एक समय हुआ करता था जब ओपी का स्निपेट एक प्रोग्रामिंग प्रोग्रामिंग बुक में एक वास्तविक उदाहरण होगा, जिसका उद्देश्य इस बात पर जोर देना था कि जावास्क्रिप्ट में 'सरणियाँ' को 0 से शुरू किया गया है, न कि 1, और इस तरह के उदाहरण के रूप में उपयोग किया जाता है। एक सामान्य 'शुरुआती गलती' (क्या आपको प्यार नहीं है कि मैं 'प्रोग्रामिंग एरर' वाक्यांश से कैसे बचता हूं ;) ): आउट-ऑफ-बाउंड्स ऐरे एक्सेस

उदाहरण 1:
0-आधारित इंडेक्सिंग (हमेशा ES262 में) का उपयोग करके 5 तत्वों के Dense Array (सन्निहित के बीच कोई अंतराल नहीं होता है) और वास्तव में प्रत्येक सूचकांक में एक तत्व होता है।

var arr_five_char=['a', 'b', 'c', 'd', 'e']; // arr_five_char.length === 5
//  indexes are:    0 ,  1 ,  2 ,  3 ,  4    // there is NO index number 5



इस प्रकार हम वास्तव में < बनाम <= (या 'एक अतिरिक्त पुनरावृत्ति') के बीच प्रदर्शन अंतर के बारे में बात नहीं कर रहे हैं, लेकिन हम बात कर रहे हैं:
'सही स्निपेट (बी) गलत स्निपेट (ए) से ज्यादा तेज क्यों चलता है?'

इसका उत्तर 2 गुना है (हालांकि ES262 भाषा कार्यान्वयनकर्ता के दृष्टिकोण से दोनों अनुकूलन के रूप हैं):

  1. डेटा-प्रतिनिधित्व: मेमोरी में आंतरिक रूप से एरे का प्रतिनिधित्व / संग्रह कैसे करें (ऑब्जेक्ट, हैशमैप, 'वास्तविक' संख्यात्मक सरणी, आदि)
  2. फ़ंक्शनल मशीन-कोड: इन 'एरे' को एक्सेस / हैंडल (पढ़ने / संशोधित) करने वाले कोड को कैसे संकलित किया जाए

आइटम 1 पर्याप्त रूप से (और सही ढंग से IMHO) स्वीकृत उत्तर द्वारा समझाया गया है, लेकिन यह केवल आइटम 2 पर 2 शब्द ('कोड') खर्च करता है : संकलन

अधिक सटीक: JIT- संकलन और इससे भी महत्वपूर्ण बात JIT- RE -Compilation!

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

अब, जावास्क्रिप्ट कोड / पुस्तकालयों / उपयोग में वृद्धि के साथ, और याद रखना कि कितने संसाधन (समय / स्मृति / आदि) एक 'वास्तविक' संकलक का उपयोग करता है, यह स्पष्ट है कि हम वेब पेज पर आने वाले उपयोगकर्ताओं को लंबे समय तक इंतजार नहीं कर सकते (और उनकी आवश्यकता है) उपलब्ध कई संसाधनों के लिए)।

निम्नलिखित सरल कार्य की कल्पना करें:

function sum(arr){
  var r=0, i=0;
  for(;i<arr.length;) r+=arr[i++];
  return r;
}

पूरी तरह से स्पष्ट है, है ना? किसी भी अतिरिक्त स्पष्टीकरण की आवश्यकता नहीं है, है ना? रिटर्न-प्रकार Number , है ना?
खैर .. नहीं, नहीं और नहीं ... यह इस बात पर निर्भर करता है कि आप नामांकित फ़ंक्शन पैरामीटर को किस तर्क से arr ...

sum('abcde');   // String('0abcde')
sum([1,2,3]);   // Number(6)
sum([1,,3]);    // Number(NaN)
sum(['1',,3]);  // String('01undefined3')
sum([1,,'3']);  // String('NaN3')
sum([1,2,{valueOf:function(){return this.val}, val:6}]);  // Number(9)
var val=5; sum([1,2,{valueOf:function(){return val}}]);   // Number(8)

समस्या देखें? फिर विचार करें कि यह केवल बड़े पैमाने पर संभावित क्रमांकन को मुश्किल से समाप्त कर रहा है ... हम यह भी नहीं जानते हैं कि फ़ंक्शन RETURN को किस प्रकार से किया जाता है जब तक कि हम ...

अब इसी फ़ंक्शन- कोड की कल्पना करें, जो वास्तव में विभिन्न प्रकारों या इनपुट के विभिन्न रूपों पर उपयोग किया जा रहा है, दोनों पूरी तरह से शाब्दिक (स्रोत कोड में) वर्णित हैं और गतिशील रूप से प्रोग्राम 'एरेज़' उत्पन्न करते हैं।

इस प्रकार, यदि आप केवल JCE ONCE के फंक्शन sum को संकलित करने के लिए थे, तो एकमात्र तरीका जो हमेशा किसी भी और सभी प्रकार के इनपुट के लिए कल्पना-परिभाषित परिणाम लौटाता है, जाहिर है, केवल सभी कल्पना-निर्धारित मुख्य और उप चरणों का प्रदर्शन करके युक्ति की पुष्टि की गारंटी दे सकता है परिणाम (एक अनाम पूर्व y2k ब्राउज़र की तरह)। कोई अनुकूलन (क्योंकि कोई धारणा नहीं) और मृत धीमी व्याख्या वाली स्क्रिप्टिंग भाषा बनी हुई है।

JIT-Compilation (जस्ट इन टाइम के रूप में JIT) वर्तमान लोकप्रिय समाधान है।

इसलिए, आप फ़ंक्शन को यह मानने के लिए शुरू करते हैं कि यह क्या करता है, रिटर्न और स्वीकार करता है।
यदि आप फ़ंक्शन गैर-कल्पना अनुरूप परिणाम (जैसे कि यह अप्रत्याशित इनपुट प्राप्त करता है) वापस करना शुरू कर सकते हैं, तो यह पता लगाने के लिए जितना संभव हो उतना सरल चेक के साथ आते हैं। फिर, पिछले संकलित परिणाम को टॉस करें और कुछ अधिक विस्तृत करने के लिए recompile, तय करें कि आपके पास पहले से मौजूद आंशिक परिणाम के साथ क्या करना है (क्या यह विश्वसनीय होने के लिए वैध है या फिर से सुनिश्चित होने के लिए गणना करें), कार्यक्रम में वापस टाई। पुनः प्रयास करें। अंत में कदम के रूप में वापस पटकथा की व्याख्या के रूप में गिरावट।

इस सब में समय लगता है!

सभी ब्राउज़र अपने इंजनों पर काम करते हैं, प्रत्येक और प्रत्येक उप-संस्करण के लिए आप चीजों को बेहतर और फिर से देखेंगे। स्ट्रिंग्स इतिहास में कुछ बिंदु पर थे, वास्तव में अपरिवर्तनीय स्ट्रिंग्स (इसलिए array.join स्ट्रिंग कॉन्सेन्टेशन की तुलना में तेज़ थी), अब हम रस्सियों (या इसी तरह) का उपयोग करते हैं जो समस्या को कम करते हैं। दोनों युक्ति-अनुरूप परिणाम लौटाते हैं और यही मायने रखता है!

लंबी कहानी छोटी: सिर्फ इसलिए कि जावास्क्रिप्ट की भाषा के शब्दार्थ को अक्सर हमारी पीठ मिल जाती है (जैसे ओपी के उदाहरण में इस मूक बग के साथ) का मतलब यह नहीं है कि 'बेवकूफ' गलतियों से कंपाइलर के तेजी से मशीन-कोड को बाहर करने की संभावना बढ़ जाती है। यह मानता है कि हमने 'आमतौर पर' सही निर्देश लिखे हैं: वर्तमान मंत्र हम 'उपयोगकर्ता' (प्रोग्रामिंग भाषा का) होना चाहिए: संकलक की मदद करें, हम जो चाहते हैं उसका वर्णन करें, सामान्य मुहावरों का पक्ष लें (मूल समझ से asm.js पर संकेत लें) क्या ब्राउज़र अनुकूलन करने की कोशिश कर सकते हैं और क्यों)।

इस वजह से, प्रदर्शन के बारे में बात करना महत्वपूर्ण है, लेकिन यह भी कि मेरा क्षेत्र क्या है (और उद्धृत करने के साथ) मैं कुछ प्रासंगिक सामग्री की ओर इशारा करना चाहता हूं।

गैर-ऑब्जेक्ट ऑब्जेक्ट गुणों और सीमा सरणी तत्वों से बाहर एक अपवाद बढ़ाने के बजाय undefined मान लौटाता है। ये गतिशील विशेषताएं जावास्क्रिप्ट में प्रोग्रामिंग को सुविधाजनक बनाती हैं, लेकिन वे जावास्क्रिप्ट को कुशल मशीन कोड में संकलित करना भी मुश्किल बनाते हैं।

...

प्रभावी JIT अनुकूलन के लिए एक महत्वपूर्ण आधार यह है कि प्रोग्रामर एक व्यवस्थित तरीके से जावास्क्रिप्ट की गतिशील सुविधाओं का उपयोग करते हैं। उदाहरण के लिए, जेआईटी संकलक इस तथ्य का फायदा उठाते हैं कि ऑब्जेक्ट गुण अक्सर किसी विशिष्ट क्रम में किसी दिए गए प्रकार के ऑब्जेक्ट में जोड़े जाते हैं या सीमा सरणी पहुंच से बाहर शायद ही कभी होते हैं। JIT संकलक रनटाइम पर कुशल मशीन कोड उत्पन्न करने के लिए इन नियमितताओं का फायदा उठाते हैं। यदि कोई कोड ब्लॉक मान्यताओं को संतुष्ट करता है, तो जावास्क्रिप्ट इंजन कुशल, उत्पन्न मशीन कोड निष्पादित करता है। अन्यथा, इंजन को धीमा कोड या प्रोग्राम की व्याख्या करने के लिए वापस गिरना चाहिए।

स्रोत:
"JITProf: पिन-पॉइंटिंग JIT-unfriendly JavaScript Code"
बर्कले प्रकाशन, 2014, लिआंग गोंग, माइकल प्रडेल, कौशिक सेन द्वारा।
http://software-lab.org/publications/jitprof_tr_aug3_2014.pdf

ASM.JS (बाउंड अरेंज एक्सेस को पसंद नहीं करता है):

आगे-समय संकलन

क्योंकि asm.js जावास्क्रिप्ट का एक सबसे बड़ा उपसमूह है, यह विनिर्देश केवल सत्यापन तर्क को परिभाषित करता है- निष्पादन शब्दार्थ केवल जावास्क्रिप्ट का है। हालाँकि, मान्य asm.js आगे से (एओटी) संकलन के लिए उत्तरदायी है। इसके अलावा, AOT कंपाइलर द्वारा उत्पन्न कोड काफी कुशल हो सकता है, जिसमें विशेषता हो सकती है:

  • पूर्णांक और फ्लोटिंग-पॉइंट संख्याओं के अनबॉक्स किए गए अभ्यावेदन;
  • रनटाइम प्रकार की जांच की अनुपस्थिति;
  • कचरा संग्रह की अनुपस्थिति; तथा
  • कुशल हीप लोड और स्टोर (कार्यान्वयन की रणनीति प्लेटफ़ॉर्म से भिन्न होती है)।

कोड जो मान्य करने में विफल रहता है उसे पारंपरिक साधनों, जैसे, व्याख्या और / या सिर्फ-समय (JIT) संकलन द्वारा निष्पादन में वापस आना चाहिए।

http://asmjs.org/spec/latest/

और अंत में https://blogs.windows.com/msedgedev/2015/05/07/bringing-asm-js-to-chakra-microsoft-edge/
सीमा-जांच को हटाते समय इंजन के आंतरिक प्रदर्शन में सुधार के बारे में एक छोटी उपधारा थी (केवल लूप के बाहर सीमा-जांच को उठाते समय पहले से ही 40% का सुधार हुआ था)।



संपादित करें:
ध्यान दें कि कई स्रोत JIT-Recompilation के विभिन्न स्तरों के बारे में व्याख्या करने के लिए नीचे बात करते हैं।

ओपी के स्निपेट के बारे में उपरोक्त जानकारी के आधार पर सैद्धांतिक उदाहरण :

  • IsPrimeDiv अदृश्य को कॉल करें
  • सामान्य मान्यताओं (जैसे सीमा से बाहर का उपयोग नहीं) का उपयोग करते हुए संकलनPrimeDiv अदृश्य है
  • काम करो
  • BAM, अचानक सरणी सीमा से बाहर (दाईं ओर) पहुंचता है।
  • बकवास, इंजन का कहना है, चलो recompile है जो अलग-अलग (कम) मान्यताओं का उपयोग करते हुए PivimeDiv अदृश्य है, और यह उदाहरण इंजन यह पता लगाने की कोशिश नहीं करता है कि क्या यह वर्तमान आंशिक परिणाम का पुन: उपयोग कर सकता है, इसलिए
  • धीमे फ़ंक्शन का उपयोग करके सभी काम को पुन: निष्पादित करें (उम्मीद है कि यह खत्म हो जाए, अन्यथा दोहराएं और इस बार कोड की व्याख्या करें)।
  • वापसी का परिणाम

इसलिए समय तब था:
पहला रन (अंत में विफल) + प्रत्येक पुनरावृत्ति आदि के लिए धीमे मशीन-कोड का उपयोग करके सभी काम फिर से कर रहा है .. इस सैद्धांतिक उदाहरण में स्पष्ट रूप से> 2 गुना अधिक समय लेता है!



संपादित 2: (अस्वीकरण: नीचे दिए गए तथ्यों में आधारित अनुमान)
जितना अधिक मैं इसके बारे में सोचता हूं, उतना ही मुझे लगता है कि यह उत्तर वास्तव में गलत स्निपेट ए (या स्निपेट बी पर प्रदर्शन-बोनस) पर इस 'जुर्माना' के लिए अधिक प्रभावी कारण बता सकता है, इस पर निर्भर करता है कि आप इसे कैसे सोचते हैं), ठीक है क्यों मैं इसे (स्निपेट ए) एक प्रोग्रामिंग त्रुटि कह रहा हूं:

यह बहुत आकर्षक है कि यह मान लिया जाए कि यह this.primes एक 'सघन व्यूह' है, जो या तो शुद्ध संख्यात्मक है

  • स्रोत-कोड में हार्ड-कोडेड शाब्दिक ('वास्तविक' सरणी बनने के लिए ज्ञात उत्कृष्ट उम्मीदवार के रूप में सब कुछ पहले से ही संकलन-समय से पहले संकलक के लिए जाना जाता है) या
  • आरोही क्रमिक क्रम में एक पूर्व-आकार ( new Array(/*size value*/) ) भरने वाले एक संख्यात्मक कार्य का उपयोग करके उत्पन्न होने वाली सबसे अधिक संभावना है ('वास्तविक' सरणी बनने के लिए एक और लंबे समय से ज्ञात उम्मीदवार)।

हम यह भी जानते हैं कि primes prime_count की लंबाई prime_count रूप में prime_count गई है ! (यह इरादे और निश्चित आकार का संकेत है)।

हम यह भी जानते हैं कि अधिकांश इंजन शुरू में Arrays को कॉपी-ऑन-संशोधित (जब ज़रूरत होती है) के रूप में पास करते हैं जो उन्हें बहुत अधिक तेजी से नियंत्रित करता है (यदि आप उन्हें नहीं बदलते हैं)।

इसलिए यह मान लेना उचित है कि ऐरे primes सबसे पहले से ही आंतरिक रूप से एक अनुकूलित सरणी है, जो निर्माण के बाद परिवर्तित नहीं होती है (कंपाइलर के लिए जानना आसान है यदि सृजन के बाद कोई कोड नहीं है) और इसलिए पहले से ही (यदि लागू हो तो) इंजन के लिए) एक अनुकूलित तरीके से संग्रहीत, बहुत ज्यादा के रूप में अगर यह एक Typed Array

जैसा कि मैंने अपने sum फ़ंक्शन उदाहरण के साथ स्पष्ट करने की कोशिश की है, जो तर्क (ओं) को आसानी से पारित हो जाते हैं जो वास्तव में होने की जरूरत है और जैसे कि उस विशेष कोड को मशीन-कोड में कैसे संकलित किया जा रहा है। String को sum फ़ंक्शन में पास करना String को परिवर्तित नहीं करना चाहिए लेकिन यह बदलना कि फ़ंक्शन JIT-Compiled कैसे है! पास करने के लिए एक ऐरे को पास करना मशीन-कोड के संस्करण के एक अलग (शायद इस प्रकार के लिए अतिरिक्त, या 'आकार' के रूप में वे इसे कहते हैं, जो पास हो गया है) को संकलित करना चाहिए।

जैसा कि यह टाइप करने के लिए थोड़े बोनकुस को लगता है कि टाइप किए गए_अरे-जैसे primes ऐरे-ऑन-द-फ्लाइंग में कुछ_लॉज करें जबकि कंपाइलर जानता है कि यह फ़ंक्शन इसे संशोधित करने वाला भी नहीं है!

इन मान्यताओं के तहत जो 2 विकल्प छोड़ता है:

  1. नंबर-क्रंचर के रूप में संकलित करें कि कोई आउट-ऑफ-बाउंड्स नहीं है, अंत में आउट-ऑफ-बाउंड्स समस्या में चलाएं, recompile और redo work (जैसा कि ऊपर 1 संपादित में सैद्धांतिक उदाहरण में उल्लिखित है)
  2. कंपाइलर पहले से ही पता चला है (या संदिग्ध?) बाउंड एसेस ऊपर-सामने और फ़ंक्शन जेआईटी-संकलित था जैसे कि तर्क पास हुआ एक स्पार्स ऑब्जेक्ट था जिसके परिणामस्वरूप धीमी कार्यात्मक मशीन-कोड होता है (क्योंकि इसमें अधिक चेक / बातचीत / ज़बरदस्ती होगी आदि।)। दूसरे शब्दों में: फ़ंक्शन को कुछ ऑप्टिमाइज़ेशन के लिए कभी भी योग्य नहीं किया गया था, इसे इस तरह संकलित किया गया था जैसे कि इसे 'स्पार्स एरे' (- जैसे) तर्क मिला हो।

मुझे अब वास्तव में आश्चर्य है कि इन 2 में से कौन सा है!


यह एक तीसरे पक्ष के लिए <script> टैग के साथ HTML दस्तावेज़ में JSON प्रतिक्रिया सम्मिलित करने के लिए कठिन बनाने के लिए होगा। याद रखें कि <script> टैग को समान मूल नीति से छूट दी गई है।





javascript v8