जोड़ा मामलों के साथ तेजी से दौड़ने के लिए जावा क्यों सन्निहित स्याही पर स्विच करता है?


276

मैं कुछ जावा कोड पर काम कर रहा हूं जिसे अत्यधिक अनुकूलित करने की आवश्यकता है क्योंकि यह मेरे मुख्य कार्यक्रम तर्क में कई बिंदुओं पर लगाए गए गर्म कार्यों में चलेगा। इस कोड के भाग में मनमाने ढंग से गैर-ऋणात्मक s को बढ़ाकर बहु doubleचर बनाना शामिल है । गुणक मान प्राप्त करने के लिए एक तेज़ तरीका (संपादित करें: लेकिन सबसे तेज़ संभव नहीं है, नीचे अपडेट 2 देखें) निम्न पर है :10int exponentswitchexponent

double multiplyByPowerOfTen(final double d, final int exponent) {
   switch (exponent) {
      case 0:
         return d;
      case 1:
         return d*10;
      case 2:
         return d*100;
      // ... same pattern
      case 9:
         return d*1000000000;
      case 10:
         return d*10000000000L;
      // ... same pattern with long literals
      case 18:
         return d*1000000000000000000L;
      default:
         throw new ParseException("Unhandled power of ten " + power, 0);
   }
}

ऊपर उल्लिखित टिप्पणी से संकेत मिलता है कि case intस्थिरांक 1 से बढ़ रहे हैं, इसलिए caseउपरोक्त कोड स्निपेट में वास्तव में 19 s हैं। जब से मैं यकीन नहीं है कि मैं वास्तव में 10 की सभी शक्तियों की आवश्यकता होगी नहीं था caseबयानों 10के माध्यम से 18, मैं कुछ पूरा 10 लाख संचालन करने के लिए समय इस के साथ तुलना microbenchmarks भाग गया switchबयान एक बनाम switchकेवल साथ caseहै 0के माध्यम से 9के साथ ( exponentकरने के लिए कम 9 या करने के लिए सीमित परेड-डाउन को तोड़ने से बचें switch)। मैं बल्कि आश्चर्यचकित हो गया (मेरे लिए, कम से कम!) परिणाम है कि switchअधिक caseबयानों के साथ लंबे समय तक वास्तव में तेजी से भाग गया।

एक लार्क पर, मैंने और भी अधिक caseएस जोड़ने की कोशिश की जो सिर्फ डमी मूल्यों को लौटाता है, और पाया कि मुझे लगभग 22-27 घोषित caseएस के साथ तेजी से चलने के लिए स्विच मिल सकता है (भले ही उन डमी मामले वास्तव में कभी हिट न हों, जबकि कोड चल रहा है )। (फिर, caseएस को पहले के caseद्वारा निरंतर वृद्धि करके एक सन्निहित फैशन में जोड़ा गया था 1।) इन निष्पादन समय के अंतर बहुत महत्वपूर्ण नहीं हैं: के exponentबीच एक यादृच्छिक के लिए 0और 10, डमी गद्देदार switchबयान 1.49 सेकंड में 10 मिलियन के निष्पादन को समाप्त करता है जो 1.54 सेकंड में अनपेड के लिए सुरक्षित करता है। संस्करण, 5ns प्रति निष्पादन की एक शानदार कुल बचत के लिए। तो, इस तरह की चीज नहीं है जो पैडिंग को लेकर जुनूनी होswitchअनुकूलन के दृष्टिकोण से प्रयास के लायक बयान। लेकिन मैं अभी भी इसे उत्सुक और प्रति-सहजता से switchपाता हूं कि यह धीमा नहीं हो जाता है (या शायद सबसे अच्छा निरंतर ओ (1) समय बनाए रखने के लिए) क्योंकि इसमें अधिक caseएस जोड़ा जाता है।

स्विचिंग बेंचमार्किंग परिणाम

ये परिणाम हैं जो मैंने बेतरतीब ढंग से उत्पन्न exponentमूल्यों पर विभिन्न सीमाओं के साथ चलने से प्राप्त किए हैं। मैंने परिणामों 1को exponentसीमा के लिए सभी तरह से शामिल नहीं किया था , लेकिन वक्र का सामान्य आकार 12-17 मामले के निशान के साथ एक रिज और 18-28 के बीच की घाटी के समान रहता है। सभी परीक्षण JUnitBenchmark में समान परीक्षण इनपुट सुनिश्चित करने के लिए यादृच्छिक मूल्यों के लिए साझा कंटेनरों का उपयोग करके चलाए गए थे। मैंने परीक्षण से लेकर सबसे छोटे switchकथन तक , और इसके विपरीत, आदेश-संबंधी परीक्षण समस्याओं की संभावना को समाप्त करने के लिए, सबसे लंबे समय तक दोनों को चलाया । अगर मैंने किसी को इन परिणामों को पुन: उत्पन्न करने की कोशिश करना चाहता है, तो मैंने अपना परीक्षण कोड एक जीथब रेपो पर डाल दिया है।

तो, यहाँ क्या हो रहा है? मेरे आर्किटेक्चर या माइक्रो-बेंचमार्क निर्माण की कुछ योनि? या जावा switchवास्तव में एक छोटे से तेजी से निष्पादित करने के 18लिए 28 caseरेंज में से यह 11करने के लिए है 17?

गितुब परीक्षण रेपो "स्विच-प्रयोग"

अद्यतन: मैंने बेंचमार्किंग लाइब्रेरी को थोड़ा साफ किया और संभावित exponentमूल्यों की एक विस्तृत श्रृंखला में कुछ आउटपुट के साथ / परिणाम में एक पाठ फ़ाइल को जोड़ा । मैंने टेस्टिंग कोड में एक विकल्प भी जोड़ा है कि यह एक Exceptionसे नहीं फेंके default, लेकिन यह परिणामों को प्रभावित नहीं करता है।

अद्यतन २: २०० ९ में इस मुद्दे की कुछ अच्छी चर्चा xkcd मंच पर यहाँ से मिली: http://forums.xkcd.com/viewtopic.php?f=11&t=33524 । उपयोग की ओपी की चर्चा Array.binarySearch()ने मुझे ऊपर दिए गए घातांक पैटर्न के एक सरल सरणी-आधारित कार्यान्वयन के लिए विचार दिया। बाइनरी खोज की कोई आवश्यकता नहीं है क्योंकि मुझे पता है कि इसमें प्रविष्टियां क्या arrayहैं। यह उपयोग करने की तुलना में लगभग 3 गुना तेज दौड़ता हुआ प्रतीत होता है switch, जाहिर है कि कुछ नियंत्रण प्रवाह की कीमत पर यह switchप्रभावित होता है। उस कोड को github repo में भी जोड़ा गया है।


64
अब सभी गोगलर्स के पास सभी switchबयानों में ठीक 22 मामले होंगे , क्योंकि यह स्पष्ट रूप से सबसे इष्टतम समाधान है। : D (इसे मेरे लीड को न दिखाएं, कृपया।)
Asteri

2
क्या आपके पास एक सरल SSCCE है? यह एक मेरे लिए संकलन नहीं है। जैसा कि मैं जावा प्रदर्शन के साथ कमजोर हूं, मैं इस पर एक शॉट लेना चाहता हूं।
मिस्टिकल

5
आपको स्ट्रिंग-आधारित मामलों के बारे में मेरे उत्तर में अनुभाग "जेवीएम में स्विच" सहायक हो सकता है। मुझे लगता है कि यहां क्या हो रहा है कि आप ए से ए lookupswitchपर स्विच कर रहे हैं tableswitch। अपने कोड को डिस्सैबल करने से javapआप सुनिश्चित हो जाएंगे।
इरिकसन

2
मैंने रेपो में / निर्भर फ़ोल्डर में निर्भरता जार जोड़ा। @Mysticial क्षमा करें, मैंने इस खरगोश के छेद को गिराते हुए पहले से ही बहुत अधिक समय बिताया है! यदि आप परीक्षण कक्षाओं से "एक्सटेंडब्ड ऐनबेन्चमार्क" निकालते हैं और "com.carrotsearch" आयात से छुटकारा पा लेते हैं, तो आप सिर्फ JUnit निर्भरता के साथ चल सकते हैं, लेकिन JIT के शोर से कुछ को छानने के लिए गाजर का सामान बहुत अच्छा है। और वार्मअप अवधि। दुर्भाग्य से मैं नहीं जानता कि इंटेलीजे के बाहर इन JUnit परीक्षणों को कैसे चलाना है।
एंड्रयू बिसेल

2
@AndrewBissell मैं आपके परिणामों को बहुत सरल बेंचमार्क के साथ पुनर्प्राप्त करने में कामयाब रहा। छोटे बनाम मध्यम आकार के प्रदर्शन के लिए शाखा बनाम तालिका कुछ हद तक स्पष्ट अनुमान था। लेकिन मेरे पास 30 मामलों में जाने के बारे में किसी और से बेहतर जानकारी नहीं है ...
रहस्यवादी

जवाबों:


228

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

हालांकि, एक बार जेआईटी अपना काम शुरू कर देता है और बाईटेकोड को असेंबली में संकलित कर tableswitchदेता है , तो निर्देश हमेशा संकेत नहीं देता है: कभी-कभी स्विच टेबल को एक lookupswitch( if/ else ifसंरचना के समान ) जैसा दिखता है ।

JIT (हॉटस्पॉट JDK 1.7) द्वारा उत्पन्न असेंबली को विघटित करने से पता चलता है कि यह १ or मामले या उससे कम होने पर, १ (से अधिक (अधिक कुशल) होने पर संकेत की एक सरणी का उपयोग करता है।

18 की इस जादुई संख्या का उपयोग करने का कारण MinJumpTableSizeजेवीएम ध्वज के डिफ़ॉल्ट मूल्य (कोड में लगभग 352 रेखा) के नीचे आना प्रतीत होता है ।

मैंने हॉटस्पॉट संकलक सूची पर मुद्दा उठाया है और यह पिछले परीक्षण की विरासत प्रतीत होती है । ध्यान दें कि जेडीके 8 में अधिक बेंचमार्किंग के बाद यह डिफ़ॉल्ट मान हटा दिया गया है

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


5 मामलों के साथ, विघटित कोड इस तरह दिखता है (सीएमपी / जेई / जेजी / जेएमपी निर्देशों को देखें, अगर / गोटो के लिए विधानसभा):

[Verified Entry Point]
  # {method} 'multiplyByPowerOfTen' '(DI)D' in 'javaapplication4/Test1'
  # parm0:    xmm0:xmm0   = double
  # parm1:    rdx       = int
  #           [sp+0x20]  (sp of caller)
  0x00000000024f0160: mov    DWORD PTR [rsp-0x6000],eax
                                                ;   {no_reloc}
  0x00000000024f0167: push   rbp
  0x00000000024f0168: sub    rsp,0x10           ;*synchronization entry
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@-1 (line 56)
  0x00000000024f016c: cmp    edx,0x3
  0x00000000024f016f: je     0x00000000024f01c3
  0x00000000024f0171: cmp    edx,0x3
  0x00000000024f0174: jg     0x00000000024f01a5
  0x00000000024f0176: cmp    edx,0x1
  0x00000000024f0179: je     0x00000000024f019b
  0x00000000024f017b: cmp    edx,0x1
  0x00000000024f017e: jg     0x00000000024f0191
  0x00000000024f0180: test   edx,edx
  0x00000000024f0182: je     0x00000000024f01cb
  0x00000000024f0184: mov    ebp,edx
  0x00000000024f0186: mov    edx,0x17
  0x00000000024f018b: call   0x00000000024c90a0  ; OopMap{off=48}
                                                ;*new  ; - javaapplication4.Test1::multiplyByPowerOfTen@72 (line 83)
                                                ;   {runtime_call}
  0x00000000024f0190: int3                      ;*new  ; - javaapplication4.Test1::multiplyByPowerOfTen@72 (line 83)
  0x00000000024f0191: mulsd  xmm0,QWORD PTR [rip+0xffffffffffffffa7]        # 0x00000000024f0140
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@52 (line 62)
                                                ;   {section_word}
  0x00000000024f0199: jmp    0x00000000024f01cb
  0x00000000024f019b: mulsd  xmm0,QWORD PTR [rip+0xffffffffffffff8d]        # 0x00000000024f0130
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@46 (line 60)
                                                ;   {section_word}
  0x00000000024f01a3: jmp    0x00000000024f01cb
  0x00000000024f01a5: cmp    edx,0x5
  0x00000000024f01a8: je     0x00000000024f01b9
  0x00000000024f01aa: cmp    edx,0x5
  0x00000000024f01ad: jg     0x00000000024f0184  ;*tableswitch
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@1 (line 56)
  0x00000000024f01af: mulsd  xmm0,QWORD PTR [rip+0xffffffffffffff81]        # 0x00000000024f0138
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@64 (line 66)
                                                ;   {section_word}
  0x00000000024f01b7: jmp    0x00000000024f01cb
  0x00000000024f01b9: mulsd  xmm0,QWORD PTR [rip+0xffffffffffffff67]        # 0x00000000024f0128
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@70 (line 68)
                                                ;   {section_word}
  0x00000000024f01c1: jmp    0x00000000024f01cb
  0x00000000024f01c3: mulsd  xmm0,QWORD PTR [rip+0xffffffffffffff55]        # 0x00000000024f0120
                                                ;*tableswitch
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@1 (line 56)
                                                ;   {section_word}
  0x00000000024f01cb: add    rsp,0x10
  0x00000000024f01cf: pop    rbp
  0x00000000024f01d0: test   DWORD PTR [rip+0xfffffffffdf3fe2a],eax        # 0x0000000000430000
                                                ;   {poll_return}
  0x00000000024f01d6: ret    

18 मामलों के साथ, विधानसभा इस तरह दिखती है (ध्यान दें कि पॉइंटर्स की सरणी का उपयोग किया जाता है और सभी तुलनाओं की आवश्यकता को दबा देता है: jmp QWORD PTR [r8+r10*1]सीधे सही गुणा पर कूदता है) - यह प्रदर्शन में सुधार का संभावित कारण है:

[Verified Entry Point]
  # {method} 'multiplyByPowerOfTen' '(DI)D' in 'javaapplication4/Test1'
  # parm0:    xmm0:xmm0   = double
  # parm1:    rdx       = int
  #           [sp+0x20]  (sp of caller)
  0x000000000287fe20: mov    DWORD PTR [rsp-0x6000],eax
                                                ;   {no_reloc}
  0x000000000287fe27: push   rbp
  0x000000000287fe28: sub    rsp,0x10           ;*synchronization entry
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@-1 (line 56)
  0x000000000287fe2c: cmp    edx,0x13
  0x000000000287fe2f: jae    0x000000000287fe46
  0x000000000287fe31: movsxd r10,edx
  0x000000000287fe34: shl    r10,0x3
  0x000000000287fe38: movabs r8,0x287fd70       ;   {section_word}
  0x000000000287fe42: jmp    QWORD PTR [r8+r10*1]  ;*tableswitch
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@1 (line 56)
  0x000000000287fe46: mov    ebp,edx
  0x000000000287fe48: mov    edx,0x31
  0x000000000287fe4d: xchg   ax,ax
  0x000000000287fe4f: call   0x00000000028590a0  ; OopMap{off=52}
                                                ;*new  ; - javaapplication4.Test1::multiplyByPowerOfTen@202 (line 96)
                                                ;   {runtime_call}
  0x000000000287fe54: int3                      ;*new  ; - javaapplication4.Test1::multiplyByPowerOfTen@202 (line 96)
  0x000000000287fe55: mulsd  xmm0,QWORD PTR [rip+0xfffffffffffffe8b]        # 0x000000000287fce8
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@194 (line 92)
                                                ;   {section_word}
  0x000000000287fe5d: jmp    0x000000000287ff16
  0x000000000287fe62: mulsd  xmm0,QWORD PTR [rip+0xfffffffffffffe86]        # 0x000000000287fcf0
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@188 (line 90)
                                                ;   {section_word}
  0x000000000287fe6a: jmp    0x000000000287ff16
  0x000000000287fe6f: mulsd  xmm0,QWORD PTR [rip+0xfffffffffffffe81]        # 0x000000000287fcf8
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@182 (line 88)
                                                ;   {section_word}
  0x000000000287fe77: jmp    0x000000000287ff16
  0x000000000287fe7c: mulsd  xmm0,QWORD PTR [rip+0xfffffffffffffe7c]        # 0x000000000287fd00
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@176 (line 86)
                                                ;   {section_word}
  0x000000000287fe84: jmp    0x000000000287ff16
  0x000000000287fe89: mulsd  xmm0,QWORD PTR [rip+0xfffffffffffffe77]        # 0x000000000287fd08
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@170 (line 84)
                                                ;   {section_word}
  0x000000000287fe91: jmp    0x000000000287ff16
  0x000000000287fe96: mulsd  xmm0,QWORD PTR [rip+0xfffffffffffffe72]        # 0x000000000287fd10
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@164 (line 82)
                                                ;   {section_word}
  0x000000000287fe9e: jmp    0x000000000287ff16
  0x000000000287fea0: mulsd  xmm0,QWORD PTR [rip+0xfffffffffffffe70]        # 0x000000000287fd18
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@158 (line 80)
                                                ;   {section_word}
  0x000000000287fea8: jmp    0x000000000287ff16
  0x000000000287feaa: mulsd  xmm0,QWORD PTR [rip+0xfffffffffffffe6e]        # 0x000000000287fd20
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@152 (line 78)
                                                ;   {section_word}
  0x000000000287feb2: jmp    0x000000000287ff16
  0x000000000287feb4: mulsd  xmm0,QWORD PTR [rip+0xfffffffffffffe24]        # 0x000000000287fce0
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@146 (line 76)
                                                ;   {section_word}
  0x000000000287febc: jmp    0x000000000287ff16
  0x000000000287febe: mulsd  xmm0,QWORD PTR [rip+0xfffffffffffffe6a]        # 0x000000000287fd30
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@140 (line 74)
                                                ;   {section_word}
  0x000000000287fec6: jmp    0x000000000287ff16
  0x000000000287fec8: mulsd  xmm0,QWORD PTR [rip+0xfffffffffffffe68]        # 0x000000000287fd38
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@134 (line 72)
                                                ;   {section_word}
  0x000000000287fed0: jmp    0x000000000287ff16
  0x000000000287fed2: mulsd  xmm0,QWORD PTR [rip+0xfffffffffffffe66]        # 0x000000000287fd40
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@128 (line 70)
                                                ;   {section_word}
  0x000000000287feda: jmp    0x000000000287ff16
  0x000000000287fedc: mulsd  xmm0,QWORD PTR [rip+0xfffffffffffffe64]        # 0x000000000287fd48
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@122 (line 68)
                                                ;   {section_word}
  0x000000000287fee4: jmp    0x000000000287ff16
  0x000000000287fee6: mulsd  xmm0,QWORD PTR [rip+0xfffffffffffffe62]        # 0x000000000287fd50
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@116 (line 66)
                                                ;   {section_word}
  0x000000000287feee: jmp    0x000000000287ff16
  0x000000000287fef0: mulsd  xmm0,QWORD PTR [rip+0xfffffffffffffe60]        # 0x000000000287fd58
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@110 (line 64)
                                                ;   {section_word}
  0x000000000287fef8: jmp    0x000000000287ff16
  0x000000000287fefa: mulsd  xmm0,QWORD PTR [rip+0xfffffffffffffe5e]        # 0x000000000287fd60
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@104 (line 62)
                                                ;   {section_word}
  0x000000000287ff02: jmp    0x000000000287ff16
  0x000000000287ff04: mulsd  xmm0,QWORD PTR [rip+0xfffffffffffffe5c]        # 0x000000000287fd68
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@98 (line 60)
                                                ;   {section_word}
  0x000000000287ff0c: jmp    0x000000000287ff16
  0x000000000287ff0e: mulsd  xmm0,QWORD PTR [rip+0xfffffffffffffe12]        # 0x000000000287fd28
                                                ;*tableswitch
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@1 (line 56)
                                                ;   {section_word}
  0x000000000287ff16: add    rsp,0x10
  0x000000000287ff1a: pop    rbp
  0x000000000287ff1b: test   DWORD PTR [rip+0xfffffffffd9b00df],eax        # 0x0000000000230000
                                                ;   {poll_return}
  0x000000000287ff21: ret    

और अंत में 30 मामलों के साथ असेंबली (नीचे) 18 मामलों के समान दिखती है, सिवाय इसके अतिरिक्त movapd xmm0,xmm1जो कोड के मध्य की ओर दिखाई देता है, जैसा @cHao द्वारा स्पॉट किया गया है - हालांकि प्रदर्शन में गिरावट का संभावित कारण यह है कि विधि बहुत अधिक है डिफॉल्ट JVM सेटिंग्स के साथ इनबिल्ट होने के लिए लंबे समय:

[Verified Entry Point]
  # {method} 'multiplyByPowerOfTen' '(DI)D' in 'javaapplication4/Test1'
  # parm0:    xmm0:xmm0   = double
  # parm1:    rdx       = int
  #           [sp+0x20]  (sp of caller)
  0x0000000002524560: mov    DWORD PTR [rsp-0x6000],eax
                                                ;   {no_reloc}
  0x0000000002524567: push   rbp
  0x0000000002524568: sub    rsp,0x10           ;*synchronization entry
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@-1 (line 56)
  0x000000000252456c: movapd xmm1,xmm0
  0x0000000002524570: cmp    edx,0x1f
  0x0000000002524573: jae    0x0000000002524592  ;*tableswitch
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@1 (line 56)
  0x0000000002524575: movsxd r10,edx
  0x0000000002524578: shl    r10,0x3
  0x000000000252457c: mulsd  xmm0,QWORD PTR [rip+0xfffffffffffffe3c]        # 0x00000000025243c0
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@364 (line 118)
                                                ;   {section_word}
  0x0000000002524584: movabs r8,0x2524450       ;   {section_word}
  0x000000000252458e: jmp    QWORD PTR [r8+r10*1]  ;*tableswitch
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@1 (line 56)
  0x0000000002524592: mov    ebp,edx
  0x0000000002524594: mov    edx,0x31
  0x0000000002524599: xchg   ax,ax
  0x000000000252459b: call   0x00000000024f90a0  ; OopMap{off=64}
                                                ;*new  ; - javaapplication4.Test1::multiplyByPowerOfTen@370 (line 120)
                                                ;   {runtime_call}
  0x00000000025245a0: int3                      ;*new  ; - javaapplication4.Test1::multiplyByPowerOfTen@370 (line 120)
  0x00000000025245a1: mulsd  xmm0,QWORD PTR [rip+0xfffffffffffffe27]        # 0x00000000025243d0
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@358 (line 116)
                                                ;   {section_word}
  0x00000000025245a9: jmp    0x0000000002524744
  0x00000000025245ae: mulsd  xmm0,QWORD PTR [rip+0xfffffffffffffe22]        # 0x00000000025243d8
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@348 (line 114)
                                                ;   {section_word}
  0x00000000025245b6: jmp    0x0000000002524744
  0x00000000025245bb: mulsd  xmm0,QWORD PTR [rip+0xfffffffffffffe1d]        # 0x00000000025243e0
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@338 (line 112)
                                                ;   {section_word}
  0x00000000025245c3: jmp    0x0000000002524744
  0x00000000025245c8: mulsd  xmm0,QWORD PTR [rip+0xfffffffffffffe18]        # 0x00000000025243e8
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@328 (line 110)
                                                ;   {section_word}
  0x00000000025245d0: jmp    0x0000000002524744
  0x00000000025245d5: mulsd  xmm0,QWORD PTR [rip+0xfffffffffffffe13]        # 0x00000000025243f0
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@318 (line 108)
                                                ;   {section_word}
  0x00000000025245dd: jmp    0x0000000002524744
  0x00000000025245e2: mulsd  xmm0,QWORD PTR [rip+0xfffffffffffffe0e]        # 0x00000000025243f8
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@308 (line 106)
                                                ;   {section_word}
  0x00000000025245ea: jmp    0x0000000002524744
  0x00000000025245ef: mulsd  xmm0,QWORD PTR [rip+0xfffffffffffffe09]        # 0x0000000002524400
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@298 (line 104)
                                                ;   {section_word}
  0x00000000025245f7: jmp    0x0000000002524744
  0x00000000025245fc: mulsd  xmm0,QWORD PTR [rip+0xfffffffffffffe04]        # 0x0000000002524408
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@288 (line 102)
                                                ;   {section_word}
  0x0000000002524604: jmp    0x0000000002524744
  0x0000000002524609: mulsd  xmm0,QWORD PTR [rip+0xfffffffffffffdff]        # 0x0000000002524410
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@278 (line 100)
                                                ;   {section_word}
  0x0000000002524611: jmp    0x0000000002524744
  0x0000000002524616: mulsd  xmm0,QWORD PTR [rip+0xfffffffffffffdfa]        # 0x0000000002524418
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@268 (line 98)
                                                ;   {section_word}
  0x000000000252461e: jmp    0x0000000002524744
  0x0000000002524623: mulsd  xmm0,QWORD PTR [rip+0xfffffffffffffd9d]        # 0x00000000025243c8
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@258 (line 96)
                                                ;   {section_word}
  0x000000000252462b: jmp    0x0000000002524744
  0x0000000002524630: movapd xmm0,xmm1
  0x0000000002524634: mulsd  xmm0,QWORD PTR [rip+0xfffffffffffffe0c]        # 0x0000000002524448
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@242 (line 92)
                                                ;   {section_word}
  0x000000000252463c: jmp    0x0000000002524744
  0x0000000002524641: movapd xmm0,xmm1
  0x0000000002524645: mulsd  xmm0,QWORD PTR [rip+0xfffffffffffffddb]        # 0x0000000002524428
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@236 (line 90)
                                                ;   {section_word}
  0x000000000252464d: jmp    0x0000000002524744
  0x0000000002524652: movapd xmm0,xmm1
  0x0000000002524656: mulsd  xmm0,QWORD PTR [rip+0xfffffffffffffdd2]        # 0x0000000002524430
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@230 (line 88)
                                                ;   {section_word}
  0x000000000252465e: jmp    0x0000000002524744
  0x0000000002524663: movapd xmm0,xmm1
  0x0000000002524667: mulsd  xmm0,QWORD PTR [rip+0xfffffffffffffdc9]        # 0x0000000002524438
                                                ;*dmul
                                                ; - javaapplication4.Test1::multiplyByPowerOfTen@224 (line 86)
                                                ;   {section_word}

[etc.]

  0x0000000002524744: add    rsp,0x10
  0x0000000002524748: pop    rbp
  0x0000000002524749: test   DWORD PTR [rip+0xfffffffffde1b8b1],eax        # 0x0000000000340000
                                                ;   {poll_return}
  0x000000000252474f: ret    

7
@ syb0rg ईमानदार होने के लिए मैं ठीक विवरण नहीं समझता; ;-)
assylias

4
महान जवाब के लिए +1! जब आप ओपी के चार्ट में "डुबकी" से बाहर निकलते हैं, तो तुलना करने के लिए 30+ मामलों के साथ कुछ अलग कर सकते हैं?
एस्टरी


2
@AndrewBissell मेरा अनुमान है कि अलग-अलग व्यवहार या तो (i) क्रॉस-आर्किटेक्चर प्रदर्शन परीक्षणों पर आधारित है, जिन्होंने दिखाया है कि पॉइंटर्स की सरणी केवल तभी प्रभावी होती है जब मामलों की संख्या 18 से अधिक हो या (ii) कोड के रूप में प्रोफाइल किया गया हो यह चलाया जाता है और प्रोफाइलर निर्धारित करता है कि रनटाइम के दौरान कौन सा दृष्टिकोण बेहतर है। मुझे इसका जवाब नहीं मिल रहा है।
assylias

3
30-केस डिस्सैडफ़ॉर्म और 18-केस एक ही अधिकतर दिखते हैं। मतभेद 11 वें मामले के बाद के अतिरिक्त अतिरिक्त अतिरिक्त फेरबदल तक सीमित प्रतीत होते हैं। यह नहीं कह सकते कि जेटर क्यों करता है; यह अनावश्यक प्रतीत होता है।
22

46

स्विच - मामला तेज है अगर मामले के मानों को एक संकीर्ण सीमा में रखा जाता है जैसे।

case 1:
case 2:
case 3:
..
..
case n:

क्योंकि, इस स्थिति में कंपाइलर स्विच स्टेटमेंट में हर केस लेग की तुलना करने से बच सकता है। कंपाइलर एक जम्प टेबल बनाता है जिसमें विभिन्न पैरों पर की जाने वाली क्रियाओं के पते होते हैं। मान जिस पर स्विच किया जा रहा है, उसे एक इंडेक्स में बदलने के लिए हेरफेर किया जाता है jump table। इस कार्यान्वयन में, स्विच स्टेटमेंट में लिया गया समय एक समान इफ-और-इफ स्टेटमेंट कैस्केड के बराबर समय से कम है। स्विच स्टेटमेंट में लिया गया समय स्विच स्टेटमेंट में केस लेग्स की संख्या से भी स्वतंत्र है।

जैसा कि संकलन अनुभाग में स्विच स्टेटमेंट के बारे में विकिपीडिया में कहा गया है ।

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


4
यह सही नहीं है। यह मामला मूल्यों के संकीर्ण या विस्तृत होने के बावजूद तेज होगा। यह ओ (1) है - कोई फर्क नहीं पड़ता कि मामले के मूल्य कैसे अलग हैं।
अनिकेत इंग

6
@Aniket: विकिपीडिया का यह लेख पढ़ें। en.wikipedia.org/wiki/Branch_table
विशाल के

14
@ अनिकेट: यह ओ (1) नहीं है यदि रेंज चौड़ी और विरल है। दो प्रकार के स्विच हैं, और यदि सीमा बहुत अधिक फैली हुई है, तो जावा इसे "टेबलस्विच" के बजाय "लुकअपस्विच" में संकलित करेगा। पूर्व की तुलना प्रति शाखा टिल की आवश्यकता है, जबकि उत्तरार्द्ध नहीं है।
cHao

4
विकिपीडिया संदर्भ खोजने के लिए एक सभ्य स्थान है, लेकिन इसे आधिकारिक स्रोत नहीं माना जाना चाहिए। आप जो कुछ भी पढ़ते हैं, वह सर्वश्रेष्ठ सेकंड-हैंड जानकारी पर होता है।
cHao

6
@ एनिकट: सभी निष्पक्षता में, डिस्चार्ज एक विशिष्ट प्लेटफॉर्म पर दिए गए जेवीएम के लिए विशिष्ट है। अन्य लोग इसका अलग-अलग अनुवाद कर सकते हैं। कुछ वास्तव में एक लुकअपस्विच के लिए हैश टेबल का उपयोग कर सकते हैं। यह अभी भी एक टेबलवॉच के रूप में अच्छा प्रदर्शन नहीं करेगा, लेकिन यह कम से कम करीब हो सकता है। यह सिर्फ JIT में अधिक समय लेगा, और इनपुट में एक हैशिंग एल्गोरिथ्म लागू करना शामिल होगा। हालाँकि, परिणामस्वरूप असेंबली कोड ज्ञानवर्धक हो सकता है, लेकिन यह तब तक आधिकारिक नहीं होता जब तक कि आप विशेष रूप से Windows x86_64 पर Hotspot v1.7.whatever के बारे में बात नहीं कर रहे हों।
21

30

उत्तर बायटेकोड में निहित है:

SwitchTest10.java

public class SwitchTest10 {

    public static void main(String[] args) {
        int n = 0;

        switcher(n);
    }

    public static void switcher(int n) {
        switch(n) {
            case 0: System.out.println(0);
                    break;

            case 1: System.out.println(1);
                    break;

            case 2: System.out.println(2);
                    break;

            case 3: System.out.println(3);
                    break;

            case 4: System.out.println(4);
                    break;

            case 5: System.out.println(5);
                    break;

            case 6: System.out.println(6);
                    break;

            case 7: System.out.println(7);
                    break;

            case 8: System.out.println(8);
                    break;

            case 9: System.out.println(9);
                    break;

            case 10: System.out.println(10);
                    break;

            default: System.out.println("test");
        }
    }       
}

बायोटेक के अनुरूप; केवल प्रासंगिक भागों को दिखाया गया है:

public static void switcher(int);
  Code:
   0:   iload_0
   1:   tableswitch{ //0 to 10
        0: 60;
        1: 70;
        2: 80;
        3: 90;
        4: 100;
        5: 110;
        6: 120;
        7: 131;
        8: 142;
        9: 153;
        10: 164;
        default: 175 }

SwitchTest22.java:

public class SwitchTest22 {

    public static void main(String[] args) {
        int n = 0;

        switcher(n);
    }

    public static void switcher(int n) {
        switch(n) {
            case 0: System.out.println(0);
                    break;

            case 1: System.out.println(1);
                    break;

            case 2: System.out.println(2);
                    break;

            case 3: System.out.println(3);
                    break;

            case 4: System.out.println(4);
                    break;

            case 5: System.out.println(5);
                    break;

            case 6: System.out.println(6);
                    break;

            case 7: System.out.println(7);
                    break;

            case 8: System.out.println(8);
                    break;

            case 9: System.out.println(9);
                    break;

            case 100: System.out.println(10);
                    break;

            case 110: System.out.println(10);
                    break;
            case 120: System.out.println(10);
                    break;
            case 130: System.out.println(10);
                    break;
            case 140: System.out.println(10);
                    break;
            case 150: System.out.println(10);
                    break;
            case 160: System.out.println(10);
                    break;
            case 170: System.out.println(10);
                    break;
            case 180: System.out.println(10);
                    break;
            case 190: System.out.println(10);
                    break;
            case 200: System.out.println(10);
                    break;
            case 210: System.out.println(10);
                    break;

            case 220: System.out.println(10);
                    break;

            default: System.out.println("test");
        }
    }       
}

बायोटेक के अनुरूप; फिर से, केवल प्रासंगिक भागों को दिखाया गया है:

public static void switcher(int);
  Code:
   0:   iload_0
   1:   lookupswitch{ //23
        0: 196;
        1: 206;
        2: 216;
        3: 226;
        4: 236;
        5: 246;
        6: 256;
        7: 267;
        8: 278;
        9: 289;
        100: 300;
        110: 311;
        120: 322;
        130: 333;
        140: 344;
        150: 355;
        160: 366;
        170: 377;
        180: 388;
        190: 399;
        200: 410;
        210: 421;
        220: 432;
        default: 443 }

पहले मामले में, संकीर्ण श्रेणियों के साथ, संकलित बायटेकोड एक का उपयोग करता है tableswitch। दूसरे मामले में, संकलित बाइटकोड एक का उपयोग करता है lookupswitch

में tableswitch, स्टैक के शीर्ष पर पूर्णांक मूल्य का उपयोग तालिका में अनुक्रमित करने के लिए किया जाता है, शाखा / कूद लक्ष्य को खोजने के लिए। इस कूद / शाखा को फिर तुरंत प्रदर्शन किया जाता है। इसलिए, यह एक O(1)ऑपरेशन है।

A lookupswitchअधिक जटिल है। इस मामले में, पूर्णांक कुंजी को तालिका में सभी कुंजी के खिलाफ तुलना करने की आवश्यकता है जब तक कि सही कुंजी नहीं मिलती है। कुंजी मिलने के बाद, जंप के लिए ब्रांच / जंप टार्गेट (कि यह कुंजी मैप की जाती है) का उपयोग किया जाता है। जिस तालिका का उपयोग किया जाता है lookupswitch, उसे क्रमबद्ध किया जाता है और सही कुंजी खोजने के लिए एक द्विआधारी-खोज एल्गोरिथ्म का उपयोग किया जा सकता है। एक द्विआधारी खोज के लिए प्रदर्शन है O(log n), और पूरी प्रक्रिया भी है O(log n), क्योंकि कूद अभी भी है O(1)। तो विरल सीमाओं के मामले में प्रदर्शन कम होने का कारण यह है कि सही कुंजी को पहले ऊपर देखना चाहिए क्योंकि आप सीधे तालिका में अनुक्रमित नहीं कर सकते हैं।

यदि विरल मान हैं और आपको केवल tableswitchउपयोग करना है, तो तालिका में अनिवार्य रूप से डमी प्रविष्टियां होंगी जो defaultविकल्प की ओर इशारा करती हैं । उदाहरण के लिए, यह सोचते हैं कि अंतिम प्रविष्टि में SwitchTest10.javaथा 21के बजाय 10, आपको मिलता है:

public static void switcher(int);
  Code:
   0:   iload_0
   1:   tableswitch{ //0 to 21
        0: 104;
        1: 114;
        2: 124;
        3: 134;
        4: 144;
        5: 154;
        6: 164;
        7: 175;
        8: 186;
        9: 197;
        10: 219;
        11: 219;
        12: 219;
        13: 219;
        14: 219;
        15: 219;
        16: 219;
        17: 219;
        18: 219;
        19: 219;
        20: 219;
        21: 208;
        default: 219 }

तो संकलक मूल रूप से अंतराल के बीच डमी प्रविष्टियों वाले इस विशाल तालिका को बनाता है, जो defaultनिर्देश के शाखा लक्ष्य की ओर इशारा करता है । यहां तक ​​कि अगर वहाँ नहीं है default, तो यह स्विच ब्लॉक के बाद निर्देश की ओर इशारा करते हुए प्रविष्टियां होगी । मैंने कुछ बुनियादी परीक्षण किए, और मैंने पाया कि अगर पिछले सूचकांक और पिछले एक ( 9) के बीच का अंतर है 35, तो यह एक के lookupswitchबजाय का उपयोग करता है tableswitch

switchकथन का व्यवहार जावा वर्चुअल मशीन विनिर्देश ( : 3.10) में परिभाषित किया गया है :

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


1
मैं इस सवाल से समझ गया कि संख्याएं हमेशा संक्रामक होती हैं लेकिन सीमा कम या ज्यादा लंबी होती है - यानी एक उदाहरण में मामले 0 से 5 तक जाते हैं जबकि दूसरे उदाहरण में वे 0 से 30 तक जाते हैं - और कोई भी उदाहरण विरल मानों का उपयोग नहीं करता है
assylias

@assylias हम्म, दिलचस्प। मुझे लगता है कि मैंने सवाल को गलत समझा। मुझे कुछ और प्रयोग करने दो। तो आप कह रहे हैं कि 0-30 से एक सन्निहित सीमा के साथ भी , कंपाइलर का उपयोग करता है lookupswitch?
विविन पालीथ

@VivinPaliath: हाँ, मेरे परीक्षणों में केस स्थिरांक हमेशा सन्निहित होते हैं, इसलिए मैं मूल रूप से [0, 1], [0, 1, 2], [0, 1, 2, 3] ... आदि पर स्विच का परीक्षण कर रहा हूँ।
एंड्रयू बिसेल

@VivinPaliath नहीं, बाइटकोड हमेशा एक टेबलवॉच का उपयोग करता है - हालांकि जेआईटी कंपाइलर टेबलवॉच को उसी तरह से असेंबली करने के लिए संकलित नहीं करता है कि इसमें कितने आइटम हैं।
assylias

6
@VivinPaliath मैं निश्चित रूप से प्रश्न को अधिक स्पष्ट रूप से लिख सकता था। जब मैं इस निम्न-स्तर के बायोटेक और असेंबली सामान से संबंधित उत्तरों का मूल्यांकन करने की बात करता हूं, तो मैं अपनी गहराई से बाहर आ जाता हूं। यह अभी भी मुझे ऐसा लगता है जैसे टेबलवॉच / लुकवाइस डिस्टिंक्शन वास्तव में यहां महत्वपूर्ण है, और तुम्हारा एकमात्र जवाब है जो अब तक उन शर्तों को नियोजित करता है (हालांकि अन्य शायद एक ही अवधारणा को विभिन्न शब्दावली के साथ स्थापित कर रहे हैं)। इसके अलावा मुझे JVM स्पेक लिंक भी पसंद है।
एंड्रयू बिसेल

19

चूँकि प्रश्न पहले से ही उत्तर दिया जा रहा है (कमोबेश), यहाँ कुछ टिप है। उपयोग

private static final double[] mul={1d, 10d...};
static double multiplyByPowerOfTen(final double d, final int exponent) {
      if (exponent<0 || exponent>=mul.length) throw new ParseException();//or just leave the IOOBE be
      return mul[exponent]*d;
}

वह कोड काफी कम IC (इंस्ट्रक्शन कैश) का उपयोग करता है और हमेशा इनबिल्ड रहेगा। कोड गर्म होने पर सरणी L1 डेटा कैश में होगी। लुकअप टेबल लगभग हमेशा एक जीत है। (esp। माइक्रोबेनचर्च पर: डी)

संपादित करें: यदि आप विधि को गर्म-गर्म करना चाहते हैं, तो गैर-तेज़ पथ throw new ParseException()को न्यूनतम के समान मानें या उन्हें स्थिर विधि से अलग करने के लिए आगे बढ़ें (इसलिए उन्हें न्यूनतम के रूप में छोटा करें)। यह throw new ParseException("Unhandled power of ten " + power, 0);एक कमजोर विचार है b / c यह कोड के लिए बहुत से inlining बजट खाता है जिसे सिर्फ व्याख्या किया जा सकता है - स्ट्रिंग कंक्रीटिंग बाइटकोड में काफी क्रिया है। अधिक जानकारी और वास्तविक मामला w / ArrayList

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.