2010-04-13 10 views
7

ऐसा लगता है कि int[] के दो एरे जोड़ने पर UInt16[] के दो सरणी जोड़ने पर सी # तेज है। यह मुझे कोई समझ नहीं आता है, क्योंकि मैंने माना होगा कि सरणी शब्द-संरेखित होंगी, और इस प्रकार int[] को CPU से कम काम की आवश्यकता होगी, नहीं?UI arr16 arrays int arrays की तुलना में तेज़ी से क्यों दिखते हैं?

मैं नीचे परीक्षण कोड भाग गया, और निम्न परिणाम है:

Int for 1000 took 9896625613 tick (4227 msec) 
UInt16 for 1000 took 6297688551 tick (2689 msec) 

परीक्षण कोड निम्नलिखित है:

  1. एक बार a और b, नामित दो सरणियों बनाता है।
  2. उन्हें एक बार यादृच्छिक डेटा से भरता है।
  3. स्टॉपवॉच शुरू करता है।
  4. a और b, आइटम-दर-आइटम जोड़ता है। यह 1000 बार किया जाता है।
  5. स्टॉपवॉच को रोकता है।
  6. रिपोर्ट करता है कि यह कितना समय लगा।

यह int[] a, b और UInt16 a,b के लिए किया जाता है। और प्रत्येक समय मैं कोड चलाता हूं, UInt16 सरणी के परीक्षण int सरणी से 30% -50% कम समय लेते हैं। क्या आप इसे मुझे समझा सकते हैं?

यहाँ कोड यदि आप अगर खुद के लिए कोशिश करना चाहते है,:

public static UInt16[] GenerateRandomDataUInt16(int length) 
{ 
    UInt16[] noise = new UInt16[length]; 
    Random random = new Random((int)DateTime.Now.Ticks); 
    for (int i = 0; i < length; ++i) 
    { 
     noise[i] = (UInt16)random.Next(); 
    } 

    return noise; 
} 

public static int[] GenerateRandomDataInt(int length) 
{ 
    int[] noise = new int[length]; 
    Random random = new Random((int)DateTime.Now.Ticks); 
    for (int i = 0; i < length; ++i) 
    { 
     noise[i] = (int)random.Next(); 
    } 

    return noise; 
} 

public static int[] AddInt(int[] a, int[] b) 
{ 
    int len = a.Length; 
    int[] result = new int[len]; 
    for (int i = 0; i < len; ++i) 
    { 
     result[i] = (int)(a[i] + b[i]); 
    } 
    return result; 
} 

public static UInt16[] AddUInt16(UInt16[] a, UInt16[] b) 
{ 
    int len = a.Length; 
    UInt16[] result = new UInt16[len]; 
    for (int i = 0; i < len; ++i) 
    { 
     result[i] = (ushort)(a[i] + b[i]); 
    } 
    return result; 
} 


public static void Main() 
{ 
    int count = 1000; 
    int len = 128 * 6000; 

    int[] aInt = GenerateRandomDataInt(len); 
    int[] bInt = GenerateRandomDataInt(len); 

    Stopwatch s = new Stopwatch(); 
    s.Start(); 
    for (int i=0; i<count; ++i) 
    { 
     int[] resultInt = AddInt(aInt, bInt); 
    } 
    s.Stop(); 
    Console.WriteLine("Int for " + count 
       + " took " + s.ElapsedTicks + " tick (" 
       + s.ElapsedMilliseconds + " msec)"); 

    UInt16[] aUInt16 = GenerateRandomDataUInt16(len); 
    UInt16[] bUInt16 = GenerateRandomDataUInt16(len); 

    s = new Stopwatch(); 
    s.Start(); 
    for (int i=0; i<count; ++i) 
    { 
     UInt16[] resultUInt16 = AddUInt16(aUInt16, bUInt16); 
    } 
    s.Stop(); 
    Console.WriteLine("UInt16 for " + count 
       + " took " + s.ElapsedTicks + " tick (" 
       + s.ElapsedMilliseconds + " msec)"); 


} 
+2

क्या आपने इनलाइन तत्वों को जोड़ने के लिए प्रयास किया - बिना एडॉक्स फ़ंक्शन को कॉल किए, सरणी पास करने और लौटने के बिना? क्या आपने अन्य आकार के सरणी का प्रयास किया था? –

+0

@Grzegorz Gierlik: वास्तव में अच्छा सवाल है। जैसा कि यह खड़ा है, 'int' रूटीन को शायद दोगुनी स्मृति आवंटित करना होगा। –

+2

वह हार्डवेयर क्या है? मैं 15650 एमएससी और 14657 एमसीईसी में पहुंचता हूं (पढ़ा: कोई महत्वपूर्ण अंतर नहीं)। मुझे संदेह है कि माइक्रोबेंमार्क आपको फेंक रहा है - जेआईटी इंजन और वीएम अनुकूलित करने के लिए कुख्यात हैं। संख्याओं को जोड़ने की गति (16/32 बिट) * वही होगी * किसी भी आधुनिक x86/x64 CPU पर। हालांकि, अधिक संख्या में कैश लाइनों को भरने और संभवतः हस्तांतरण के लिए अधिक बस की आवश्यकता के मामले में बड़ी संख्या में एक छोटा जुर्माना हो सकता है। –

उत्तर

6

क्या होता है कि आप एक रिसाव अमूर्तता देखते हैं। UInt16 आधा स्मृति लेता है जो int करता है (16 बनाम 32 बिट)।

इसका मतलब है कि int16 सरणी द्वारा कब्जा कर लिया गया स्मृति क्षेत्र int32 करता है जो क्षेत्रफल का आधा हिस्सा लेता है। तो उस क्षेत्र में से अधिक प्रोसेसर कैश में फिट हो सकता है और इस प्रकार बहुत जल्दी पहुंचा जा सकता है।

आप उस प्रोसेसर पर उस कोड को आजमा सकते हैं जिसमें अधिक कैश है और अंतर छोटा होने की संभावना है।

भी बहुत बड़े सरणी के साथ प्रयास करें।

1) तुम भी परिणामी की पीढ़ी समय कर रहे हैं array..so यह देखने के लिए यह कितना समय ले लिया दिलचस्प होगा करने के लिए सिर्फ परिणाम सरणी पारित हो कि बनाने बनाम जोड़ने के लिए कारकों में से

+0

इसके विपरीत, इसे एक छोटे से ऐरे के साथ आज़माएं जो एक कैश लाइन के अंदर फिट हो। –

2

सरणी शब्द गठबंधन कर रहे हैं, लेकिन वहाँ कोई कारण नहीं क्यों सरणी में प्रविष्टियों शब्द गठबंधन होना चाहिए।

1

बस एक एसडब्ल्यूएजी: UInt16 सरणी के छोटे मेमोरी उपयोग में स्मृति विशेषताओं में सुधार हुआ है (जीसी, कैश, जो और जानता है)। चूंकि बहुत सारे आवंटन प्रतीत नहीं होते हैं, मुझे लगता है कि कैश मुख्य कारक है।

इसके अलावा, आपको यह ध्यान रखना चाहिए कि बेंचमार्किंग एक मुश्किल व्यवसाय हो सकता है - ऐसा लगता है कि आपके समय शायद कुछ जेआईटी संकलन सहित हैं, जो परिणाम को कम कर सकते हैं। आप UInt16 सरणी के साथ int सरणी का परीक्षण करने के आदेश को उलटाने का प्रयास कर सकते हैं और देख सकते हैं कि समय के साथ पालन करें या नहीं।

जॉन स्कीट ने एक सरल बेंचमार्क ढांचे को (या था) किया था जब उसने इन प्रभावों को ध्यान में रखने की कोशिश की थी। मुझे नहीं पता कि यह अभी भी उपलब्ध है (या यहां तक ​​कि लागू भी है); शायद वह टिप्पणी करेगा।

1

युगल पीछे

2) यह देखना दिलचस्प होगा कि आईएल क्या उत्पन्न हुआ है। चूंकि आपका कोड बहुत सरल है (पुनरावृत्त और जोड़ता है), संकलक इसे अनुकूलित कर सकता है, शायद एक बड़े रजिस्टर में एकाधिक uint16 को भर रहा है और प्रति निर्देश

+1

मैंने इसे परावर्तक में चेक किया, और यह नहीं हो रहा है कि क्या हो रहा है। कोड एल्गोरिदमिक रूप से लगभग समान है। सभी परिचालन समान हैं लेकिन उनके उचित डेटा प्रकारों के लिए समायोजित हैं। 'UInt16' मामले में' add' के बाद 'conv.u2' ऑपरेशन को जोड़ने का एकमात्र महत्वपूर्ण अंतर है (' add' एक int देता है, मुझे लगता है - इसे वापस करने के लिए प्रलेखन नहीं मिल रहा है, हालांकि यह खड़ा है कारण यह है कि सी # काम भी करता है)। यदि आईएल में अंतर था, तो मैं उम्मीद करता हूं कि 'UInt16' संस्करण धीमा हो जाएगा, उस अतिरिक्त रूपांतरण के लिए धन्यवाद। कैश मिस सिद्धांत पर मेरी शर्त है। – Dathan

1

मैं .NET में विशेषज्ञ नहीं हूं लेकिन मैं दो की जांच करूंगा बातें:

  1. बड़ा सरणी (प्रकार int के एन तत्वों) पासिंग और अधिक समय तो एन ushort तत्वों की सरणी लेता है। यह विभिन्न आकार के सरणी और कोडिंग की शैली का उपयोग करके परीक्षण किया जा सकता है - प्रश्न पर मेरी टिप्पणी देखें)। आपके परीक्षणों से संख्या इस सिद्धांत के अनुरूप है :)। जाँच बह निकला बिना -
  2. दो ushort चर जोड़ना प्रकार int का परिणाम के साथ दो int जोड़ने के रूप में लागू किया जा सकता। और मुझे लगता है कि कोड कोड में अपवाद (ओवरफ्लो अपवाद सहित) समय लेने वाली कार्य है। इसे .NET दस्तावेज़ में चेक किया जा सकता है।
+1

एफवाईआई, वीएस 2008 उपरोक्त संकलन करते समय 'add' IL ऑपरेशन का उपयोग करता है, और [सीआईएल स्पेक] (http://download.microsoft.com/download/7/3/3/733AD403-90B2-4064-A81E- 01035A7FE13C/MS% 20 पार्टिशन% 20III.pdf) कहता है कि 'add' ऑपरेशन ओवरफ़्लो की जांच नहीं करता है। – Dathan

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