2012-06-04 12 views
8

यह एक Seq मॉड्यूल के iter और map कार्यों बहुत धीमी Array और List मॉड्यूल समकक्ष की तुलना में किया जा रहा है के बारे में मेरे पिछले question अप करने के लिए का पालन करें।सीक मॉड्यूल में कुछ फ़ंक्शन क्यों अनुकूलित किए गए हैं जबकि अन्य F # में नहीं थे?

स्रोत को देखते हुए, मैं देख सकता हूँ कि कुछ कार्यों ऐसे isEmpty और length के रूप में एक बहुत ही सरल प्रकार की जांच करता है IEnumerator का उपयोग कर का सहारा से पहले सरणियों और सूचियों के लिए अनुकूलन करने के लिए।

[<CompiledName("IsEmpty")>] 
let isEmpty (source : seq<'T>) = 
    checkNonNull "source" source 
    match source with 
    | :? ('T[]) as a -> a.Length = 0 
    | :? list<'T> as a -> a.IsEmpty 
    | :? ICollection<'T> as a -> a.Count = 0 
    | _ -> 
     use ie = source.GetEnumerator() 
     not (ie.MoveNext()) 

[<CompiledName("Length")>] 
let length (source : seq<'T>) = 
    checkNonNull "source" source 
    match source with 
    | :? ('T[]) as a -> a.Length 
    | :? ('T list) as a -> a.Length 
    | :? ICollection<'T> as a -> a.Count 
    | _ -> 
     use e = source.GetEnumerator() 
     let mutable state = 0 
     while e.MoveNext() do 
      state <- state + 1; 
     state 

iter एक ही दृष्टिकोण बेहद अपने प्रदर्शन में सुधार करने, जब मैं iter समारोह में यह अंतर्निहित संस्करण पर महत्वपूर्ण लाभ प्रस्तुत shadowed किया जा सकता है के मामले में:

[<CompiledName("Iterate")>] 
let iter f (source : seq<'T>) = 
    checkNonNull "source" source 
    use e = source.GetEnumerator() 
    while e.MoveNext() do 
     f e.Current; 

मेरे सवाल यह है कि Seq मॉड्यूल में कुछ कार्यों को विशिष्ट संग्रह प्रकारों (सरणी, सूची < टी> इत्यादि) के उपयोग के लिए अनुकूलित किया गया था। iter और nth जैसे अन्य फ़ंक्शन कैसे नहीं थे एक समान तरीके से समयबद्ध?

इसके अलावा, map फ़ंक्शन के मामले में, @mausch ने बताया, क्या Enumerable.Select (नीचे देखें) के समान दृष्टिकोण को नियोजित करना संभव नहीं है और विभिन्न संग्रह प्रकारों के लिए विशेष इटरेटर्स का निर्माण करना संभव नहीं है?

public static IEnumerable<TResult> Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector) 
    { 
     if (source == null) 
     throw Error.ArgumentNull("source"); 
     if (selector == null) 
     throw Error.ArgumentNull("selector"); 
     if (source is Enumerable.Iterator<TSource>) 
     return ((Enumerable.Iterator<TSource>) source).Select<TResult>(selector); 
     if (source is TSource[]) 
     return (IEnumerable<TResult>) new Enumerable.WhereSelectArrayIterator<TSource, TResult>((TSource[]) source, (Func<TSource, bool>) null, selector); 
     if (source is List<TSource>) 
     return (IEnumerable<TResult>) new Enumerable.WhereSelectListIterator<TSource, TResult>((List<TSource>) source, (Func<TSource, bool>) null, selector); 
     else 
     return (IEnumerable<TResult>) new Enumerable.WhereSelectEnumerableIterator<TSource, TResult>(source, (Func<TSource, bool>) null, selector); 
    } 

अग्रिम में बहुत धन्यवाद।

+3

मुझे यकीन नहीं है कि आपको इस तरह के 'क्यों' प्रश्न का एक सभ्य उत्तर मिलेगा; मेरा सबसे अच्छा _guess_ बस डेवलपर समय की कमी है। – ildjarn

उत्तर

1

सतह पर, Seq.length/isEmpty में टाइप-चेक गलतियों की तरह लगते हैं। मुझे लगता है कि अधिकांश Seq फ़ंक्शन ऑर्थोगोनैलिटी के लिए ऐसे चेक नहीं करते हैं: List/Array मॉड्यूल में टाइप-विशिष्ट संस्करण पहले से मौजूद हैं। उन्हें डुप्लिकेट क्यों करें?

उन चेक LINQ में अधिक समझ में आता है क्योंकि यह केवल IEnumerable का उपयोग करता है।

+5

कम से कम 'Seq.length' के मामले में, अनुकूलन संभावित रूप से ओ (एन) ऑपरेशन को ओ (1) ऑपरेशन में बदल सकता है। मुझे संदेह है कि यह एक गलती है। – kvb

+0

@ केवीबी: यह केवल ओपी के बिंदु पर जोर देता है। क्या आप उसके सवाल का जवाब दे सकते हैं? यदि आपको लगता है कि वे वैध अनुकूलन हैं (उपलब्ध प्रकार-विशिष्ट विकल्पों के बावजूद) उन्हें अन्य कार्यों पर लागू क्यों न करें? वे मेरे लिए अनावश्यक और अनावश्यक लगते हैं। – Daniel

+4

नहीं, मुझे जवाब नहीं पता है, लेकिन 'Seq.iter',' Seq.map', आदि के मामले में एक प्रकार का परीक्षण करने से एल्गोरिदम की एसिम्प्टोटिक जटिलता नहीं बदलेगी, केवल स्थिर कारक। – kvb

4

आईटीईआर एक ही दृष्टिकोण के मामले में मुझे लगता है कि यह वह जगह है जहाँ अपने प्रश्न का उत्तर है बेहद इसके प्रदर्शन

सुधार करने के लिए किया जा सकता है। आपका परीक्षण कृत्रिम है, और वास्तव में इन विधियों के किसी वास्तविक दुनिया के उदाहरणों का परीक्षण नहीं करता है। ms में समय में मतभेद प्राप्त करने के लिए आपने इन विधियों के 10,000,000 पुनरावृत्तियों का परीक्षण किया।

आइटम की लागत प्रति वापस करने के लिए परिवर्तित, यहाँ वे हैं:

  Array List 
Seq.iter 4 ns 7 ns 
Seq.map 20 ns 91 ns 

इन विधियों आम तौर पर, एक बार संग्रह प्रति उपयोग किया जाता है इस लागत अर्थ अपने एल्गोरिदम प्रदर्शन के लिए एक अतिरिक्त रैखिक कारक है। सबसे बुरी स्थिति में आप सूची में 100 ns प्रति आइटम से कम खो रहे हैं (यदि आप प्रदर्शन के बारे में अधिक ख्याल रखते हैं तो आपको इसका उपयोग नहीं करना चाहिए)।

length के मामले में इसकी तुलना करें जो सामान्य मामले में हमेशा रैखिक होता है। इस अनुकूलन को जोड़कर आप किसी ऐसे व्यक्ति को भारी लाभ प्रदान करते हैं जो लंबाई को मैन्युअल रूप से कैश करना भूल जाता है लेकिन सौभाग्य से हमेशा एक सूची दी जाती है।

इसी तरह आप कई बार isEmpty पर कॉल कर सकते हैं, और एक और वस्तु निर्माण जोड़ना मूर्खतापूर्ण है अगर आप सीधे पूछ सकते हैं। (यह एक तर्क जितना मजबूत नहीं है)

ध्यान में रखना एक और बात यह है कि उन तरीकों में से कोई भी वास्तव में आउटपुट के एक से अधिक तत्वों को नहीं देखता है। आप निम्न कोड को क्या करना चाहते हैं (वाक्यविन्यास त्रुटियों या अनुपलब्ध विधियों को छोड़कर)

type Custom() = 
    interface IEnumerable with 
    member x.GetEnumerator() = 
     return seq { 
     yield 1 
     yield 2 
     } 
    interface IList with 
    member x.Item with 
     get(index) = index 
    member x.Count = 12 

let a = Custom() 
a |> Seq.iter (v -> printfn (v.ToString())) 
+1

आपका उदाहरण दर्शाता है कि मैं इसे एक गलत तरीके क्यों मानता हूं। यह कम से कम आश्चर्य के सिद्धांत का पालन करता है कि 'सेक' केवल 'seq <_>', सरणी पर 'ऐरे' और 'सूची <_>' पर 'सूची' पर निर्भर करता है। यह चुनने के लिए प्रोग्रामर पर निर्भर है कि कौन सा इंटरफ़ेस इस्तेमाल किया जाना चाहिए। – Daniel

+1

@Daniel: यह प्रदर्शन में सुधार के लिए एक कार्यान्वयन विस्तार है। बीटीडब्लू, एफ # कार्यान्वयन केवल 'सूची' पर 'IList' पर निर्भर नहीं है, जिसे इन अजीब स्थितियों को बनाने के लिए अधिभारित नहीं किया जा सकता है। – Guvante

+0

नहीं, लेकिन आप 'ICollection <_>' और 'seq <_>' का उपयोग करके कुछ समान रूप से विरोध कर सकते हैं। – Daniel

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

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