यह F # कोड इतना धीमा क्यों है?


127

C # और F # में एक लेवेंसहेटिन कार्यान्वयन। लगभग 1500 वर्णों के दो तारों के लिए C # संस्करण 10 गुना तेज है। सी #: 69 एमएस, एफ # 867 एमएस। क्यों? जहां तक ​​मैं बता सकता हूं, वे ठीक यही काम करते हैं? कोई फर्क नहीं पड़ता कि यह एक रिलीज या डिबग बिल्ड है।

संपादित करें: यदि कोई विशेष रूप से एडिट डिस्टेंस कार्यान्वयन की तलाश में यहां आता है, तो उसे तोड़ दिया जाता है। वर्किंग कोड यहाँ है

C # :

private static int min3(int a, int b, int c)
{
   return Math.Min(Math.Min(a, b), c);
}

public static int EditDistance(string m, string n)
{
   var d1 = new int[n.Length];
   for (int x = 0; x < d1.Length; x++) d1[x] = x;
   var d0 = new int[n.Length];
   for(int i = 1; i < m.Length; i++)
   {
      d0[0] = i;
      var ui = m[i];
      for (int j = 1; j < n.Length; j++ )
      {
         d0[j] = 1 + min3(d1[j], d0[j - 1], d1[j - 1] + (ui == n[j] ? -1 : 0));
      }
      Array.Copy(d0, d1, d1.Length);
   }
   return d0[n.Length - 1];
}

एफ # :

let min3(a, b, c) = min a (min b c)

let levenshtein (m:string) (n:string) =
   let d1 = Array.init n.Length id
   let d0 = Array.create n.Length 0
   for i=1 to m.Length-1 do
      d0.[0] <- i
      let ui = m.[i]
      for j=1 to n.Length-1 do
         d0.[j] <- 1 + min3(d1.[j], d0.[j-1], d1.[j-1] + if ui = n.[j] then -1 else 0)
      Array.blit d0 0 d1 0 n.Length
   d0.[n.Length-1]

7
इनलाइन का उपयोग करके प्रदर्शन में क्या अंतर है?
gradbot

जवाबों:


202

समस्या यह है कि min3फ़ंक्शन को एक जेनेरिक फ़ंक्शन के रूप में संकलित किया जाता है जो जेनेरिक तुलना का उपयोग करता है (मुझे लगा कि यह सिर्फ इसका उपयोग करता है IComparable, लेकिन यह वास्तव में अधिक जटिल है - यह एफ # प्रकारों के लिए संरचनात्मक तुलना का उपयोग करेगा और यह काफी जटिल तर्क है)।

> let min3(a, b, c) = min a (min b c);;
val min3 : 'a * 'a * 'a -> 'a when 'a : comparison

C # संस्करण में, फ़ंक्शन सामान्य नहीं है (यह सिर्फ लेता है int)। आप प्रकार एनोटेशन जोड़कर एफ # संस्करण में सुधार कर सकते हैं (सी # में समान चीज़ प्राप्त करने के लिए):

let min3(a:int, b, c) = min a (min b c)

... या के min3रूप inlineमें बना (जो मामले में, यह विशेष रूप से intजब इस्तेमाल किया जाएगा):

let inline min3(a, b, c) = min a (min b c);;

str300 की एक यादृच्छिक स्ट्रिंग के लिए, मुझे निम्नलिखित नंबर मिलते हैं:

> levenshtein str ("foo" + str);;
Real: 00:00:03.938, CPU: 00:00:03.900, GC gen0: 275, gen1: 1, gen2: 0
val it : int = 3

> levenshtein_inlined str ("foo" + str);;
Real: 00:00:00.068, CPU: 00:00:00.078, GC gen0: 0, gen1: 0, gen2: 0
val it : int = 3

1
F # को min3 को एक फंक्शन के रूप में संकलित क्यों नहीं करता है जो int लेता है? यह पहले से ही ऐसा करने के लिए संकलन समय पर पर्याप्त प्रकार की जानकारी जानता है। यह कैसे काम करेगा अगर यह min3 C ++ टेम्प्लेट फंक्शन था, तो मैं थोड़ा हैरान हूं कि F # ऐसा क्यों नहीं करता।
शशंग

42
F # इसे जितना संभव हो उतना सामान्य हो जाता है, उदाहरण के लिए "सभी प्रकार के लिए X जो कि तुलना का समर्थन करता है"। inlineC ++ टेम्पलेट की तरह काम करता है, जो intकॉल साइट पर आधारित होगा ।
ब्रायन

13
C ++ टेम्पलेट अनिवार्य रूप से F # के रूप में व्यवहार करते हैं inline। डिफ़ॉल्ट व्यवहार भिन्न होने का कारण यह है क्योंकि यह .Net जेनेरिक बनाता है जो रनटाइम द्वारा नियंत्रित किया जाता है (और, यकीनन, जेनेरिक न्यूमेरिक कोड लिखने के लिए इतना बढ़िया नहीं है)। हालांकि, F # में C ++ व्यवहार का उपयोग करते हुए, कोड ब्लोट का नेतृत्व किया जाता है, क्योंकि F # बहुत अधिक जेनेरिक का उपयोग करता है।
टॉमस पेट्रिसक

4
सी ++ टेम्पलेट शब्दार्थ सी + + में भी कोड ब्लोट को जन्म दे सकता है, और समय पर परेशानी से बचने के लिए रन-टाइम तंत्र का उपयोग करने के लिए स्विच करने के लिए एक सुविधाजनक तरीके की कमी है। हालांकि, कोड ब्लोट का डर सामान्य रूप से तर्कहीन है - आम तौर पर, C ++ टेम्पलेट अच्छी तरह से काम करते हैं।
स्टीव 314

@ स्टीव ३१४: आमतौर पर सभी प्रकार के कोड को रिफलेक्ट करने से बचना आसान होता है, जो एक आश्रित प्रकार का उपयोग नहीं करता है, इसलिए उस कोड को अलग-अलग तात्कालिकता के लिए दोहराया नहीं जाता है।
इलडार्जन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.