2012-06-09 11 views
13

पढ़ना शुरुआत एफ # रॉबर्ट पिकरिंग मैं निम्नलिखित पैराग्राफ पर ध्यान केंद्रित:CLR बनाम OCaml अपवाद भूमि के ऊपर

प्रोग्रामर्स एक OCaml पृष्ठभूमि से आने वाले सावधान रहना चाहिए जब एफ # में अपवाद का उपयोग कर। सीएलआर के आर्किटेक्चर की वजह से, अपवाद फेंकना काफी महंगी है - ओकैमल की तुलना में काफी महंगा है। यदि आप बहुत सारे अपवाद फेंकते हैं, तो यह निर्धारित करने के लिए कि प्रदर्शन लागत इसके लायक है या नहीं, अपने कोड को सावधानीपूर्वक प्रोफ़ाइल दें। यदि लागत बहुत अधिक है, तो कोड को उचित रूप से संशोधित करें।

क्यों, CLR की वजह से, एक अपवाद फेंकने और अधिक महंगा है, तो F#OCaml में से है? और इस मामले में उचित रूप से कोड को संशोधित करने का सबसे अच्छा तरीका क्या है?

+1

पिछली [ओकैमल अपवादों की हल्केपन की चर्चा] [http://stackoverflow.com/questions/8564025/ocaml-internals-exceptions) –

+0

@ जेफरीस्कोफिल्ड धन्यवाद, हालांकि मैं सीएलआर से संबंधित पहलुओं में अधिक रुचि रखता हूं । – gliderkite

उत्तर

7

रीड पहले से ही समझाया गया है कि क्यों .NET अपवाद OCaml अपवादों से अलग व्यवहार करते हैं। सामान्यतः, .NET अपवाद केवल असाधारण परिस्थितियों के लिए उपयुक्त हैं और उस उद्देश्य के लिए डिज़ाइन किए गए हैं। ओकैमल में अधिक हल्के मॉडल हैं और इसलिए इन्हें कुछ नियंत्रण प्रवाह पैटर्न को लागू करने के लिए भी उपयोग किया जाता है।

ओकैमल में एक ठोस उदाहरण देने के लिए आप लूप को तोड़ने के लिए अपवाद का उपयोग कर सकते हैं। उदाहरण के लिए, कहें कि आपके पास test फ़ंक्शन है जो परीक्षण करता है कि कोई संख्या एक संख्या है जिसे हम चाहते हैं। 1 से 100 तक नंबर और रिटर्न पहले नंबर से मेल खाते पर निम्नलिखित दोहराता:

// Simple exception used to return the result 
exception Returned of int 

try 
    // Iterate over numbers and throw if we find matching number 
    for n in 0 .. 100 do 
    printfn "Testing: %d" n 
    if test n then raise (Returned n) 
    -1     // Return -1 if not found 
with Returned r -> r // Return the result here 

अपवाद के बिना इसे लागू करने के लिए, आप दो विकल्प हैं। आप एक पुनरावर्ती समारोह समान व्यवहार है कि लिख सकता है - अगर आप find 0 फोन (और यह सी # में return nfor अंदर पाश का उपयोग कर के रूप में मूलतः एक ही आईएल कोड को संकलित किया गया है):

let rec find n = 
    printfn "Testing: %d" n 
    if n > 100 then -1 // Return -1 if not found 
    elif test n then n // Return the first found result 
    else find (n + 1) // Continue iterating 

एन्कोडिंग पुनरावर्ती कार्यों का उपयोग कर सकते हैं थोड़ा लंबा हो, लेकिन आप एफ # लाइब्रेरी द्वारा प्रदान किए गए मानक कार्यों का भी उपयोग कर सकते हैं। कोड को फिर से लिखने का यह सबसे अच्छा तरीका है जो नियंत्रण प्रवाह के लिए ओकैमल अपवादों का उपयोग करेगा। इस मामले में, आप लिख सकते हैं:

// Find the first value matching the 'test' predicate 
let res = seq { 0 .. 100 } |> Seq.tryFind test 
// This returns an option type which is 'None' if the value 
// was not found and 'Some(x)' if the value was found. 
// You can use pattern matching to return '-1' in the default case: 
match res with 
| None -> -1 
| Some n -> n 

यदि आप विकल्प प्रकार से परिचित नहीं हैं, तो कुछ प्रारंभिक सामग्री देखें। F# wikibook has a good tutorial और MSDN documentation has useful examples भी।

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

संपादित करें: मैं वास्तविक प्रदर्शन के बारे में उत्सुक था। Seq.tryFind का उपयोग करने वाला संस्करण अधिक कुशल है यदि इनपुट (सूची आवंटन की लागत के कारण) के बजाय आलसी उत्पन्न अनुक्रम seq { 1 .. 100 } है। इन परिवर्तनों, और test समारोह है कि 25 वीं तत्व देता है, समय मेरी मशीन पर कोड 100000 बार चलाने के लिए आवश्यक के साथ है:

exceptions 2.400sec 
recursion 0.013sec 
Seq.tryFind 0.240sec 

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

+0

+1 मुझे निश्चित रूप से अपनी पुस्तक पढ़नी होगी। सिर्फ एक चीज, क्यों सेक से फ़ंक्शन का उपयोग सीधे रिकर्सन का उपयोग करने से कम कुशल हो सकता है? – gliderkite

+0

@gliderkite मैं एक आदिम परीक्षण चलाता हूं और कुछ प्रदर्शन जानकारी जोड़ता हूं। –

12

सीएलआर में अपवाद बहुत समृद्ध हैं, और बहुत सारी जानकारी प्रदान करते हैं। रिको मारियानी ने सीएलआर में the cost of exceptions पर एक (पुराना, लेकिन अभी भी प्रासंगिक) पोस्ट पोस्ट किया है, जिसमें से कुछ विवरण हैं।

इस तरह, सीएलआर में अपवाद फेंकने से ओकैमल समेत कुछ अन्य वातावरणों की तुलना में अधिक सापेक्ष लागत होती है।

और इस मामले में उचित रूप से कोड को संशोधित करने का सबसे अच्छा तरीका क्या है?

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

+1

'विकल्प' प्रकार यहां अत्यंत प्रासंगिक है। – ildjarn

+0

@ildjarn हाँ, बिल्कुल। अक्सर, आप अपवादों का उपयोग करने के बजाय 'none' का उपयोग कर सकते हैं। –

+0

मुझे खेद है, मैं नहीं जानता 'कोई नहीं' और 'विकल्प' प्रकार। क्या आप इन प्रासंगिक पहलुओं को विस्तारित कर सकते हैं? – gliderkite

8

क्यों, CLR की वजह से, एक अपवाद फेंक अगर एफ # OCaml में से ज्यादा महंगा है?

OCaml को नियंत्रण प्रवाह के रूप में अपवादों के उपयोग के लिए अत्यधिक अनुकूलित किया गया था। इसके विपरीत, .NET में अपवादों को बिल्कुल अनुकूलित नहीं किया गया है।

ध्यान दें कि प्रदर्शन अंतर विशाल है। OCaml में F # के मुकाबले अपवाद लगभग 600 × तेज हैं। मेरे बेंचमार्क के अनुसार, सी ++ भी इस संबंध में ओकैमल की तुलना में 6 × धीमी है।

भले ही .NET अपवाद कथित तौर पर अधिक प्रदान करते हैं (ओकैमल स्टैक निशान प्रदान करता है, आप और क्या चाहते हैं?) मैं किसी भी कारण से नहीं सोच सकता कि वे जितना धीमे हो सकते हैं।

और इस मामले में उचित रूप से कोड को संशोधित करने का सबसे अच्छा तरीका क्या है?

एफ # में, आपको "कुल" फ़ंक्शन लिखना चाहिए। इसका मतलब है कि आपके फ़ंक्शन को यूनियन प्रकार का मान वापस करना चाहिए जो परिणाम के प्रकार को इंगित करता है, उदा। सामान्य या असाधारण।

विशेष रूप से, findtryFind के लिए कॉल कि option प्रकार है कि या तो Some मूल्य या None अगर प्रमुख तत्व संग्रह में मौजूद नहीं थे का मान प्रदान के साथ प्रतिस्थापित किया जाना चाहिए करने के लिए कहता है।

+0

'मैं किसी भी कारण से नहीं सोच सकता कि वे इतने धीमे क्यों हो सकते हैं।' यह वही है जो मैं पूछ रहा हूं। लिंक [_ की लागत का अपवाद_] (http://blogs.msdn.com/b/ricom/archive/2003/12/19/44697.aspx) रीड कॉपसी द्वारा पोस्ट किया गया यह बहुत धीमा क्यों बताता है कि यह धीमा क्यों है। – gliderkite

+0

@gliderkite वह आलेख बताता है कि .NET धीमा है और वर्कअराउंड का वर्णन करता है (.NET के इस भाग से बचें) लेकिन मुझे नहीं लगता कि यह क्यों वर्णन करता है। मुझे लगता है कि जवाब बस इतना है कि माइक्रोसॉफ्ट ने इसे अनुकूलित करने पर कभी भी परेशान नहीं किया। –

+0

'मुझे नहीं लगता कि यह वर्णन करता है कि दुर्भाग्यवश ऐसा क्यों है, लेकिन मुझे नहीं लगता कि माइक्रोसॉफ्ट ने इसे अनुकूलित करने पर कभी भी परेशान नहीं किया है, इस तरह से क्या लाभ प्राप्त होगा? – gliderkite