2009-12-22 12 views
19

this answer पर एक टिप्पणी में (जो प्रदर्शन के लिए पूर्णांक गुणा/विभाजन पर बिट-शिफ्ट ऑपरेटरों का उपयोग करने का सुझाव देता है), मैंने पूछा कि यह वास्तव में तेज़ होगा या नहीं। मेरे दिमाग के पीछे एक विचार है कि पर कुछ स्तर पर, कुछ >> 1 और / 2 काम करने के लिए पर्याप्त चालाक होगा। हालांकि, अब मैं सोच रहा हूं कि यह वास्तव में सच है, और यदि यह है, तो यह किस स्तर पर होता है।क्या सीआई/सीआईएल के लिए जेआईटीटर द्वारा उत्पादित मूल कोड देखने का कोई तरीका है?

IL_0000: ldarg.0 
    IL_0001: ldc.i4.2 
    IL_0002: div 
    IL_0003: ret 
} // end of method Program::Divider 

बनाम

IL_0000: ldarg.0 
    IL_0001: ldc.i4.1 
    IL_0002: shr 
    IL_0003: ret 
} // end of method Program::Shifter 

तो सी # संकलक div उत्सर्जित करती है:

एक परीक्षण कार्यक्रम दो तरीकों कि क्रमशः विभाजित और उनके तर्क बदलाव के लिए (पर optimize के साथ) में निम्न तुलनात्मक सीआईएल का उत्पादन या shr निर्देश, चतुर होने के बिना। अब मैं वास्तविक x86 असेंबलर देखना चाहता हूं जो कि जिटर उत्पन्न करता है, लेकिन मुझे नहीं पता कि यह कैसे करना है। क्या यह भी संभव है?

संपादित जवाब के लिए

निष्कर्ष

धन्यवाद जोड़ने के लिए है, क्योंकि यह है कि डीबगर विकल्प के बारे में महत्वपूर्ण जानकारी निहित nobugz से एक स्वीकार कर लिया है। क्या अंत में मेरे लिए काम किया है:

  • स्विच विन्यास
  • रिलीज करने Tools | Options | Debugger में, बंद 'मॉड्यूल लोड पर JIT अनुकूलन को रोकें' (यानी हम चाहते हैं कि JIT अनुकूलन की अनुमति)
  • एक ही जगह है, बंद 'बस मेरे कोड सक्षम'
  • एक Debugger.Break() बयान रखो कहीं
  • विधानसभा का निर्माण (यानी हम डिबग करने के लिए सभी कोड चाहते हैं)
  • .exe चलाएँ, और जब यह टूट जाता है, उदाहरण के वी.एस. मौजूदा का उपयोग कर डिबग
  • अब Disassembly खिड़की आप वास्तविक x86 कि निष्पादित किया जाना है

परिणाम कम से कम कहने के लिए शिक्षाप्रद थे जा रहा है पता चलता - यह पता चला कि जिटर वास्तव में अंकगणित कर सकता है! Disassembly विंडो से संपादित नमूने यहां दिए गए हैं। विभिन्न -Shifter विधियों को >> का उपयोग करके दो की शक्तियों से विभाजित किया गया है; विभिन्न -Divider तरीकों /

Console.WriteLine(string.Format(" 
    {0} 
    shift-divided by 2: {1} 
    divide-divided by 2: {2}", 
    60, TwoShifter(60), TwoDivider(60))); 

00000026 mov   dword ptr [edx+4],3Ch 
... 
0000003b mov   dword ptr [edx+4],1Eh 
... 
00000057 mov   dword ptr [esi+4],1Eh 

दोनों स्थिर-विभाजित-दर-2 तरीकों केवल inlined नहीं किया गया है का उपयोग कर पूर्णांकों से विभाजित है, लेकिन वास्तविक संगणना घबराना

Console.WriteLine(string.Format(" 
    {0} 
    divide-divided by 3: {1}", 
    60, ThreeDivider(60))); 

00000085 mov   dword ptr [esi+4],3Ch 
... 
000000a0 mov   dword ptr [esi+4],14h 

के साथ भी यही द्वारा किया गया हो स्थिर-विभाजित-दर-3।

Console.WriteLine(string.Format(" 
    {0} 
    shift-divided by 4: {1} 
    divide-divided by 4 {2}", 
    60, FourShifter(60), FourDivider(60))); 

000000ce mov   dword ptr [esi+4],3Ch 
... 
000000e3 mov   dword ptr [edx+4],0Fh 
... 
000000ff mov   dword ptr [esi+4],0Fh 

और स्थैतिक रूप से विभाजित -4 -4।

सबसे अच्छा:

Console.WriteLine(string.Format(" 
    {0} 
    n-divided by 2: {1} 
    n-divided by 3: {2} 
    n-divided by 4: {3}", 
    60, Divider(60, 2), Divider(60, 3), Divider(60, 4))); 

0000013e mov   dword ptr [esi+4],3Ch 
... 
0000015b mov   dword ptr [esi+4],1Eh 
... 
0000017b mov   dword ptr [esi+4],14h 
... 
0000019b mov   dword ptr [edi+4],0Fh 

यह inlined है और फिर इन सभी स्थिर डिवीजनों अभिकलन!

लेकिन यदि परिणाम स्थिर नहीं है तो क्या होगा? मैंने कंसोल से एक पूर्णांक पढ़ने के लिए कोड में जोड़ा। यह वही है कि पर डिवीजनों के लिए उत्पादन होता है:

Console.WriteLine(string.Format(" 
    {0} 
    shift-divided by 2: {1} 
    divide-divided by 2: {2}", 
    i, TwoShifter(i), TwoDivider(i))); 

00000211 sar   eax,1 
... 
00000230 sar   eax,1 

तो के बावजूद कोल इंडिया अलग किया जा रहा है, घबराना कोई जानता है कि 2 से विभाजित राइट स्थानांतरण 1.

Console.WriteLine(string.Format(" 
    {0} 
    divide-divided by 3: {1}", i, ThreeDivider(i))); 

00000283 idiv eax के द्वारा होता है, ECX

और यह जानता है कि आप द्वारा 3.

Console.WriteLine(string.Format(" 
    {0} 
    shift-divided by 4: {1} 
    divide-divided by 4 {2}", 
    i, FourShifter(i), FourDivider(i))); 

000002c5 sar   eax,2 
... 
000002ec sar   eax,2 

विभाजित करने के लिए विभाजित करने के लिए है और यह kno ws 4 से कि विभाजन सही-स्थानांतरण द्वारा 2.

अंत में

(सबसे अच्छा फिर से!) है

Console.WriteLine(string.Format(" 
    {0} 
    n-divided by 2: {1} 
    n-divided by 3: {2} 
    n-divided by 4: {3}", 
    i, Divider(i, 2), Divider(i, 3), Divider(i, 4))); 

00000345 sar   eax,1 
... 
00000370 idiv  eax,ecx 
... 
00000395 sar   esi,2 

यह विधि inlined और सबसे अच्छा तरीका है काम करने के लिए बाहर काम किया है, statically- के आधार पर उपलब्ध तर्क। अच्छा लगा।


तो हाँ, C# और 86 के बीच ढेर में कहीं, कुछ पर्याप्त चतुर बाहर काम करने कि >> 1 और / 2 समान हैं। और इसने मेरे दिमाग में मेरे विचार में और भी अधिक वजन दिया है कि सी # कंपाइलर, जेआईटर और सीएलआर को जोड़कर एक बहुत छोटी चाल से हम आर बनाते हैं, हम विनम्र अनुप्रयोग प्रोग्रामर के रूप में कोशिश कर सकते हैं :)

+0

क्या आप हमारे निष्कर्षों को हम सभी के लिए पोस्ट कर सकते हैं? धन्यवाद :) – flesh

उत्तर

8

जब तक आप डीबगर को कॉन्फ़िगर नहीं करते हैं तब तक आपको सार्थक परिणाम नहीं मिलेंगे। उपकरण + विकल्प, डिबगिंग, सामान्य, "मॉड्यूल लोड पर जेआईटी अनुकूलन को दबाएं" बंद करें। रिलीज मोड कॉन्फ़िगरेशन पर स्विच करें। एक नमूना टुकड़ा:

static void Main(string[] args) { 
    int value = 4; 
    int result = divideby2(value); 
} 

आप इसे कर रहे हैं disassembly के इस तरह दिखता है सही है, तो:

00000000 ret 

आप अभिव्यक्ति के लिए मजबूर करने JIT अनुकूलक मूल्यांकन किया जाना मूर्ख करना होगा। कंसोल का उपयोग करना।WriteLine (चर) मदद कर सकते हैं। तो आपको ऐसा कुछ देखना चाहिए:

0000000a mov   edx,2 
0000000f mov   eax,dword ptr [ecx] 
00000011 call  dword ptr [eax+000000BCh] 

युप, इसने संकलन समय पर परिणाम का मूल्यांकन किया। बहुत अच्छी तरह से काम करता है, है ना।

+0

"मॉड्यूल लोड पर जेआईटी ऑप्टिमाइज़ेशन को दबाएं" पर आपको सार्थक नतीजे क्यों नहीं मिलेगा? मेरी व्याख्या यह है कि इसे सक्षम करने का मतलब सिर्फ अनुकूलित/"रिलीज" देशी कोड को डिबग करना है। – Justin

3

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

फिर, अपने प्रोग्राम में ब्रेकपॉइंट सेट करें और जब यह टूट जाए, तो इस Disassembly कमांड पर क्लिक करें। वीएस आपको डिस्सेम्बल मशीन कोड दिखाएगा। एक डिवाइडर-विधि के लिए

उदाहरण आउटपुट:

public static int Divider(int intArg) 
    { 
00000000 push  ebp 
00000001 mov   ebp,esp 
00000003 push  edi 
00000004 push  esi 
00000005 push  ebx 
00000006 sub   esp,34h 
00000009 mov   esi,ecx 
0000000b lea   edi,[ebp-38h] 
0000000e mov   ecx,0Bh 
00000013 xor   eax,eax 
00000015 rep stos dword ptr es:[edi] 
00000017 mov   ecx,esi 
00000019 xor   eax,eax 
0000001b mov   dword ptr [ebp-1Ch],eax 
0000001e mov   dword ptr [ebp-3Ch],ecx 
00000021 cmp   dword ptr ds:[00469240h],0 
00000028 je   0000002F 
0000002a call  6BA09D91 
0000002f xor   edx,edx 
00000031 mov   dword ptr [ebp-40h],edx 
00000034 nop    
    return intArg/2; 
00000035 mov   eax,dword ptr [ebp-3Ch] 
00000038 sar   eax,1 
0000003a jns   0000003F 
0000003c adc   eax,0 
0000003f mov   dword ptr [ebp-40h],eax 
00000042 nop    
00000043 jmp   00000045 
    } 
+2

उस आदेश को वीएस में जोड़ने की आवश्यकता नहीं है, यह डीबग-> विंडोज मेनू पर है। –

2

जब आप डीबगिंग रहे हैं (और केवल जब तुम डिबगिंग रहे हैं) बस पर डीबग पर क्लिक करें - विंडोज - Disassembly या इसी शॉर्टकट Ctrl + Alt + डी

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

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