2010-07-13 9 views
9

चूंकि एफ # 2.0 वीएस -2010 का हिस्सा बन गया है, इसलिए मैं एफ # में रूचि लेता हूं। मुझे आश्चर्य हुआ कि इसका उपयोग करने का क्या मतलब है। मैं थोड़ा सा पढ़ूंगा और मैंने फ़ंक्शन कॉलिंग को मापने के लिए एक बेंचमार्क बनाया था। मैं एकरमैन के समारोह :)नेट फ़ंक्शन कॉलिंग का प्रदर्शन (सी # एफ #) वीएस सी ++

सी #

sealed class Program 
{ 
    public static int ackermann(int m, int n) 
    { 
     if (m == 0) 
      return n + 1; 
     if (m > 0 && n == 0) 
     { 
      return ackermann(m - 1, 1); 
     } 
     if (m > 0 && n > 0) 
     { 
      return ackermann(m - 1, ackermann(m, n - 1)); 
     } 
     return 0; 
    } 

    static void Main(string[] args) 
    { 

     Stopwatch stopWatch = new Stopwatch(); 

     stopWatch.Start(); 
     Console.WriteLine("C# ackermann(3,10) = " + Program.ackermann(3, 10)); 
     stopWatch.Stop(); 

     Console.WriteLine("Time required for execution: " + stopWatch.ElapsedMilliseconds + "ms"); 
     Console.ReadLine(); 
    } 
} 

सी ++

class Program{ 
public: 
static inline int ackermann(int m, int n) 
{ 
    if(m == 0) 
     return n + 1; 
    if (m > 0 && n == 0) 
    { 
     return ackermann(m - 1, 1); 
    } 
    if (m > 0 && n > 0) 
    { 
     return ackermann(m - 1, ackermann(m, n - 1)); 
    } 
    return 0; 
} 
}; 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
clock_t start, end; 

    start = clock(); 
std::cout << "CPP: ackermann(3,10) = " << Program::ackermann(3, 10) << std::endl; 
end = clock(); 
std::cout << "Time required for execution: " << (end-start) << " ms." << "\n\n"; 
int i; 
std::cin >> i; 
return 0; 
} 

एफ #

// Ackermann 
let rec ackermann m n = 
    if m = 0 then n + 1 
    elif m > 0 && n = 0 then ackermann (m - 1) 1 
    elif m > 0 && n > 0 then ackermann (m - 1) (ackermann m (n - 1)) 
    else 0 

open System.Diagnostics; 
let stopWatch = Stopwatch.StartNew() 
let x = ackermann 3 10 
stopWatch.Stop(); 

printfn "F# ackermann(3,10) = %d" x 
printfn "Time required for execution: %f" stopWatch.Elapsed.TotalMilliseconds 

जावा

public class Main 
{ 
public static int ackermann(int m, int n) 
{ 
if (m==0) 
    return n + 1; 
if (m>0 && n==0) 
{ 
return ackermann(m - 1,1); 
} 
if (m>0 && n>0) 
{ 
    return ackermann(m - 1,ackermann(m,n - 1)); 
} 
return 0; 
} 

    public static void main(String[] args) 
    { 
    System.out.println(Main.ackermann(3,10)); 
    } 
} 
का इस्तेमाल किया है 363,210

एक तो
सी # = 510ms
C++ = 130ms
एफ # = 185ms
जावा = Stackoverflow :)

यह एफ # की शक्ति (कोड की एक छोटी राशि को छोड़कर) है हम उपयोग करना चाहते हैं नेट और थोड़ा तेज़ निष्पादन प्राप्त करें? क्या मैं इनमें से किसी भी कोड को अनुकूलित कर सकता हूं (विशेष रूप से एफ #)?

अद्यतन। मैंने कंसोल से छुटकारा पा लिया। राइटलाइन और डीबगर के बिना सी # कोड चलाएं: सी # = 400 एमएमएस

+9

बेंचमार्क चलाने से पहले एक बार अपने विधि निष्पादित। आपके समय में जेआईटी को मध्यवर्ती भाषा में लिया गया समय शामिल है। साथ ही, अपने बेंचमार्क के बाहर Console.WriteLine() जैसी विधियां लें, क्योंकि वे गंभीर रूप से धीमे हैं। –

+1

"अपने विधि एक बार बेंचमार्क चलाने से पहले निष्पादित। आपका बार समय मध्यवर्ती भाषा जीत के लिए ले जाया शामिल किया है।" यह है? मैं dd = ackermann 3 10 जोड़ने देता हूं और मुझे 7 एम अतिरिक्त देता है। सी # फ्लॉप परिवर्तन :) के लिए "इसके अलावा,, Console.WriteLine() अपने बेंचमार्क के बाहर की तरह तरीकों क्योंकि वे गंभीरता से धीमी गति से कर रहे हैं।" अच्छा विचार लेकिन –

+0

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

उत्तर

14

मेरा मानना ​​है कि इस मामले में, सी # और एफ # के बीच का अंतर पूंछ-कॉल अनुकूलन के लिए धन्यवाद है।

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

ackermann के अपने कार्यान्वयन में, कॉल की दो पूंछ कॉल कर रहे हैं और उनमें से केवल एक नहीं है (एक जहाँ परिणाम एक और ackermann कॉल करने के लिए एक तर्क के रूप पारित हो जाता है)। इसका मतलब है कि एफ # संस्करण वास्तव में कम से कम एक "कॉल" निर्देश (ज्यादा?) का आह्वान करता है।

सामान्य रूप से, प्रदर्शन प्रोफ़ाइल लगभग सी # के प्रदर्शन प्रोफ़ाइल के समान ही है। काफी कुछ बनाम सी # प्रदर्शन यहां एफ # से संबंधित पोस्ट नहीं हैं:

+2

मैं सी # में एक रिकर्सिव वंश पार्सर लिख रहा हूं और अक्सर मुझे एफ # पूंछ कॉल अनुकूलन की कमी के लिए। – ChaosPandion

+0

thx बहुत कुछ। सी ++ के बारे में क्या? –

+6

कैओसपैंडियन: आप सी # में एक पार्सर लिख रहे हैं और एकमात्र कारण है कि आपको एफ # का उपयोग न करने का अफसोस है कि यह पूंछ कॉल को अनुकूलित नहीं कर सकता है? वास्तव में? यही कारण है? – Gabe

1

एफ # के लिए आप इनलाइन कीवर्ड की कोशिश कर सकते हैं।

इसके अलावा, जैसा कि पिछले पोस्टर का उल्लेख है, सी # और एफ # संस्करण कंसोल के कारण भिन्न हैं। राइटलाइन स्टेटमेंट्स।

+2

फ़ंक्शन इनलाइन नहीं हो सकता है और रिक –

+3

आप एक पुनरावर्ती फ़ंक्शन को कैसे रेखांकित करेंगे? – Gabe

+0

रिकर्सिव गाँठ को खोलें और अनलोल करने के लिए 'इनलाइन' का उपयोग करें। –

6

यह फ़ंक्शन कॉल को कम करने के लिए एक आम तरीका है क्योंकि यह फ़ंक्शन कॉलिंग से संबंधित है।

आप इस प्रकार के रिकर्सिव फ़ंक्शन को स्मोइज़ेशन (कैशिंग) के माध्यम से तेज़ी से चला सकते हैं। आप इसे सी # (example) में भी कर सकते हैं।) मुझे 18ms मिल गया।

open System.Collections.Generic 

let memoize2 f = 
    let cache = Dictionary<_, _>() 
    fun x y -> 
     if cache.ContainsKey (x, y) then 
      cache.[(x, y)] 
     else 
      let res = f x y 
      cache.[(x, y)] <- res 
      res 

// Ackermann 
let rec ackermann = 
    memoize2 (fun m n -> 
     if m = 0 then n + 1 
     elif m > 0 && n = 0 then ackermann (m - 1) 1 
     elif m > 0 && n > 0 then ackermann (m - 1) (ackermann m (n - 1)) 
     else 0 
    ) 
4

सीधे प्रश्न से संबंधित नहीं है, लेकिन पर्याप्त दिलचस्प उल्लेख करने के लिए: मेरे पीसी पर इस संस्करण

let rec ackermann2 m n = 
    match m,n with 
    | 0,0 -> 0 
    | 0,n -> n+1 
    | m,0 -> ackermann2 (m-1) 1 
    | m,n -> ackermann2 (m-1) (ackermann2 m (n-1)) 

कोशिश (VS2010, एफ # इंटरैक्टिव) यह लगभग 50% अपने संस्करण की तुलना में तेजी जब परिकलन करते हैं एकरमैन 3 12.

मैं काफी क्यों इस तरह के एक प्रदर्शन अंतर है व्याख्या नहीं कर सकते। मुझे लगता है कि एफ # कंपाइलर के साथ कुछ विवरण है जो मिलान अभिव्यक्ति का अनुवाद अगर कथन की श्रृंखला के बजाय एक स्विच स्टेटमेंट में कर रहा है। पहले (एम = 0, एन = 0) परीक्षण डालने से भी मदद मिल सकती है।

+1

पैटर्न मिलान कंपाइलर को अनावश्यकता को कम करने के लिए परीक्षणों को पुन: स्थापित करना चाहिए और मूल कोड ने 'm> 0' परीक्षण दो बार किया था। –

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