दिलचस्प पोस्ट
OpenSSL और openssl.conf फ़ाइल को पढ़ने में त्रुटि Directory.EnumerateFiles => अनधिकृत प्रवेश एक्सेस क्या SQL Server में dateTime और dateTime में कोई अंतर है? डुप्लिकेट फ़ाइलें कॉपी की गईं (एंड्रॉइड स्टूडियो 0.4.0) कैसे एक आइपीथॉन जादू चलाने के लिए एक स्क्रिप्ट से (या एक पायथन स्क्रिप्ट के समय) Backstack में टुकड़े के साथ onSaveInstanceState का उपयोग करना? SQLite में मान्य तालिका नाम क्या हैं? 'स्क्रिप्ट' टैग के लिए आवश्यक 'प्रकार' विशेषता है? क्यों `या` के साथ कई मूल्यों के आधार पर कोई चर जाँचता है, केवल प्रथम मान की जांच करें? सीसीवी को एन्कोड करने के लिए कुशलतापूर्वक पार्स करने का सबसे मजबूत तरीका क्या है? एंड्रॉइड: मैंने अपनी एंड्रॉइड कुंजी स्टोर खो दिया है, मुझे क्या करना चाहिए? NSURL कनेक्शन कनेक्शन भेजेंअसमनसुरक्षा: कतार: पूरा होन्डलर: एक पंक्ति में एकाधिक अनुरोध करना? पॉलीगॉन एल्गोरिदम में पॉइंट किसी दशमलव में सी # परिणामों में दोहरे अंकों के रूपांतरण का अंतर फ़ंक्शन फ़ाइल की फ़ंक्शन सूची

पूंछ कॉल opcode उत्पन्न

जिज्ञासा से मैं सी # का उपयोग कर पूंछ कॉल opcode उत्पन्न करने की कोशिश कर रहा था। फिबिनाची एक आसान है, इसलिए मेरा सी # उदाहरण ऐसा दिखता है:

private static void Main(string[] args) { Console.WriteLine(Fib(int.MaxValue, 0)); } public static int Fib(int i, int acc) { if (i == 0) { return acc; } return Fib(i - 1, acc + i); } 

अगर मैं इसे रिलीज़ में बना देता हूं और इसे डिबगिंग के बिना चलाने देता है तो मुझे स्टैक ओवरफ्लो नहीं मिलता है ऑप्टिमाइज़ेशन के बिना डिबग करना या चलाना और मुझे एक स्टैक ओवरफ्लो मिलता है, जिसका अर्थ है कि पूंछ कॉल काम कर रहा है जब ऑप्टिमाइज़ेशन पर रिलीज होने पर (जो कि मुझे उम्मीद थी)।

इस के लिए MSIL इस तरह दिखता है:

 .method public hidebysig static int32 Fib(int32 i, int32 acc) cil managed { // Method Start RVA 0x205e // Code Size 17 (0x11) .maxstack 8 L_0000: ldarg.0 L_0001: brtrue.s L_0005 L_0003: ldarg.1 L_0004: ret L_0005: ldarg.0 L_0006: ldc.i4.1 L_0007: sub L_0008: ldarg.1 L_0009: ldarg.0 L_000a: add L_000b: call int32 [ConsoleApplication2]ConsoleApplication2.Program::Fib(int32,int32) L_0010: ret } 

मैं एक पूंछ opcode देखने की उम्मीद थी, प्रति एमएसडीएन , लेकिन यह वहाँ नहीं है यह मुझे सोच रहा था कि क्या जेआईटी कंपाइलर इसे डालने के लिए जिम्मेदार था? मैंने एनजीएन को एनजीएन ( ngen install <exe> का उपयोग करने की कोशिश की, इसे प्राप्त करने के लिए विंडो असेंबलियों की सूची में नेविगेट करना) और इसे आईएलपीटी में बैक अप लोड कर दिया लेकिन यह मेरे लिए यही दिखता है:

 .method public hidebysig static int32 Fib(int32 i, int32 acc) cil managed { // Method Start RVA 0x3bfe // Code Size 17 (0x11) .maxstack 8 L_0000: ldarg.0 L_0001: brtrue.s L_0005 L_0003: ldarg.1 L_0004: ret L_0005: ldarg.0 L_0006: ldc.i4.1 L_0007: sub L_0008: ldarg.1 L_0009: ldarg.0 L_000a: add L_000b: call int32 [ConsoleApplication2]ConsoleApplication2.Program::Fib(int32,int32) L_0010: ret } 

मैं अभी भी इसे देख नहीं है

मुझे पता है कि F # हैंडल की पूंछ अच्छी तरह से कॉल करती है, इसलिए मैं उस सी की तुलना करना चाहता हूं जो # मेरा F # उदाहरण ऐसा दिखता है:

 let rec fibb i acc = if i = 0 then acc else fibb (i-1) (acc + i) Console.WriteLine (fibb 3 0) 

और फाइब विधि के लिए उत्पन्न आईएल इस तरह दिखता है:

 .method public static int32 fibb(int32 i, int32 acc) cil managed { // Method Start RVA 0x2068 // Code Size 18 (0x12) .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationArgumentCountsAttribute::.ctor(int32[]) = { int32[](Mono.Cecil.CustomAttributeArgument[]) } .maxstack 5 L_0000: nop L_0001: ldarg.0 L_0002: brtrue.s L_0006 L_0004: ldarg.1 L_0005: ret L_0006: ldarg.0 L_0007: ldc.i4.1 L_0008: sub L_0009: ldarg.1 L_000a: ldarg.0 L_000b: add L_000c: starg.s acc L_000e: starg.si L_0010: br.s L_0000 } 

जो, आईएलएसपी के अनुसार, इस के बराबर है:

 [Microsoft.FSharp.Core.CompilationArgumentCounts(Mono.Cecil.CustomAttributeArgument[])] public static int32 fibb(int32 i, int32 acc) { label1: if !(((i != 0))) { return acc; } (i - 1); i = acc = (acc + i);; goto label1; } 

तो F # गोटो स्टेटमेंट्स का उपयोग करके पूंछ कॉल उत्पन्न? यह मैं उम्मीद नहीं कर रहा था।

मैं कहीं भी पूंछ कॉल पर भरोसा करने की कोशिश नहीं कर रहा हूँ, लेकिन मैं सिर्फ उत्सुक हूँ, जहां बिल्कुल कि opcode सेट करता है? यह कैसे सी # कर रहा है?

वेब के समाधान से एकत्रित समाधान "पूंछ कॉल opcode उत्पन्न"

सी # संकलक आपको पूंछ कॉल अनुकूलन के बारे में कोई गारंटी नहीं देता है क्योंकि C # प्रोग्राम आमतौर पर लूप का उपयोग करते हैं और इसलिए वे पूंछ कॉल अनुकूलन पर भरोसा नहीं करते। तो, सी # में, यह केवल एक जेआईटी अनुकूलन है जो हो सकता है या नहीं हो सकता है (और आप उस पर भरोसा नहीं कर सकते)।

F # संकलक को कार्यात्मक कोड को संभाल करने के लिए डिज़ाइन किया गया है जो पुनरावर्ती का उपयोग करता है और इसलिए यह पूंछ कॉल के बारे में आपको निश्चित गारंटी देता है। यह दो तरह से किया जाता है:

  • यदि आप एक पुनरावर्ती समारोह लिखते हैं जो स्वयं को कॉल करता है (जैसे आपका fib ) तो कंपाइलर उसे एक फंक्शन में बदल देता है जो शरीर में लूप का उपयोग करता है (यह एक सरल अनुकूलन है और उत्पादित कोड पूंछ-कॉल की तुलना में तेज़ है)

  • यदि आप एक अधिक जटिल स्थिति में एक रिकर्सिव कॉल का उपयोग करते हैं (फिर से निरंतरता शैली का उपयोग करते समय, जहां एक तर्क के रूप में कार्य पारित किया जाता है), तो कंपाइलर पूंछ-कॉल अनुदेश उत्पन्न करता है जो कि जेआईटी को बताता है कि उसे पूंछ कॉल का उपयोग करना चाहिए

दूसरे मामले का एक उदाहरण के रूप में, निम्नलिखित साधारण F # फ़ंक्शन को संकलित करें (एफ # डीबगिंग को सरल बनाने के लिए डीबग मोड में ऐसा नहीं करता है, इसलिए आपको रिलीज़ मोड की आवश्यकता हो सकती है या जोड़ सकते हैं – --tailcalls+ ):

 let foo a cont = cont (a + 1) 

फ़ंक्शन केवल फ़ंक्शन cont को एक के द्वारा बढ़ाए गए पहले तर्क के साथ कॉल करता है। निरंतरता पारित करने की शैली में, आपके पास इस तरह के कॉल का एक लंबा अनुक्रम होता है, इसलिए अनुकूलन महत्वपूर्ण है (आप पूंछ कॉलों के कुछ संचालन के बिना इस शैली का उपयोग नहीं कर सकते हैं)। उत्पन्न आईएल कोड इस तरह दिखता है:

 IL_0000: ldarg.1 IL_0001: ldarg.0 IL_0002: ldc.i4.1 IL_0003: add IL_0004: tail. // Here is the 'tail' opcode! IL_0006: callvirt instance !1 class [FSharp.Core] Microsoft.FSharp.Core.FSharpFunc`2<int32, !!a>::Invoke(!0) IL_000b: ret 

पूंछ कॉल ऑप्टिमाइजेशन के साथ। नेट में स्थिति काफी जटिल है। जहाँ तक मुझे पता है, यह इस तरह है:

  • सी # संकलक tail. कभी नहीं फेंकना होगा opcode और यह भी खुद पूंछ कॉल अनुकूलन कभी नहीं करेंगे
  • एफ # संकलक कभी कभी tail. उत्सर्जन करता है tail. opcode और कभी कभी पूंछ कॉल अनुकूलन खुद को आईआईएल emitting द्वारा कि पुनरावर्ती नहीं है।
  • सीएलआर tail. सम्मान करेगा tail. opcode अगर यह मौजूद है और 64-बिट CLR कभी-कभी पूंछ कॉल ऑप्टिमाइजेशन भी करते हैं, जब opcode मौजूद नहीं है।

इसलिए, आपके मामले में, आपने tail. नहीं देखा था tail. सी # संकलक द्वारा उत्पन्न आईएल में opcode, क्योंकि यह ऐसा नहीं करता है लेकिन विधि पूंछ-कॉल अनुकूलित थी, क्योंकि सीएलआर कभी-कभी ऐसा करता है कि बिना opcode भी।

और एफ # मामले में, आपने देखा है कि च # संकलक स्वयं द्वारा अनुकूलन करता था

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

आपको यह देखने के लिए जनरेटेड मशीन कोड को देखना होगा, डीबग करें + विंडोज + डिसैंपैपर अधिक आवश्यकता के साथ कि आप रिलीज़ बिल्ड कोड को देखकर ऐसा करते हैं जो टूल्स + विकल्प, डीबगिंग, सामान्य से जेट ऑप्टिमाइज़ेशन को दबाए बिना अन्तर्निर्मित

X64 कोड इस तरह दिखता है:

  public static int Fib(int i, int acc) { if (i == 0) { 00000000 test ecx,ecx 00000002 jne 0000000000000008 return acc; 00000004 mov eax,edx 00000006 jmp 0000000000000011 } return Fib(i - 1, acc + i); 00000008 lea eax,[rcx-1] 0000000b add edx,ecx 0000000d mov ecx,eax 0000000f jmp 0000000000000000 // <== here!!! 00000011 rep ret 

चिह्नित निर्देश नोट करें, एक कॉल के बजाय एक छलांग। काम पर यह पूंछ कॉल अनुकूलन है .NET में एक ज्वालामुखी है कि 32-बिट x 86 ज़ीटर यह अनुकूलन नहीं करता है। बस एक करने वाली वस्तु है कि वे शायद कभी भी आसपास नहीं आ जाएंगे। किसने एफ # कंपाइलर लेखकों को समस्या को अनदेखा न करें और Opcodes का उत्सर्जन करें। टेलिकॉल आपको इस उत्तर में प्रलेखित जिटर द्वारा निष्पादित अन्य अनुकूलन मिलेगा।