हमारे द्वारा Microsoft के लिए छोड़े गए फ्रेमवर्क उपयोगकर्ताओं के लिए एक और संस्करण यहां दिया गया है। यह पैनोसो थोफ के समाधान कीArray.Clear
तुलना में 4 गुना तेज और तेज है और एरिक जे और पेटार पेत्रोव के समानांतर एक - बड़े सरणियों के लिए दो गुना तेज है।
पहले मैं आपको फ़ंक्शन का पूर्वज प्रस्तुत करना चाहता हूं, क्योंकि इससे कोड को समझना आसान हो जाता है। प्रदर्शन-वार यह पैनोसो थियोफ के कोड के साथ बराबरी पर है, और कुछ चीजों के लिए जो पहले से ही पर्याप्त हो सकती हैं:
public static void Fill<T> (T[] array, int count, T value, int threshold = 32)
{
if (threshold <= 0)
throw new ArgumentException("threshold");
int current_size = 0, keep_looping_up_to = Math.Min(count, threshold);
while (current_size < keep_looping_up_to)
array[current_size++] = value;
for (int at_least_half = (count + 1) >> 1; current_size < at_least_half; current_size <<= 1)
Array.Copy(array, 0, array, current_size, current_size);
Array.Copy(array, 0, array, current_size, count - current_size);
}
जैसा कि आप देख सकते हैं, यह पहले से ही आरंभिक हिस्से के दोहराए जाने पर आधारित है। यह सरल और कुशल है, लेकिन यह आधुनिक मेमोरी आर्किटेक्चर के लिए चलता है। इसलिए एक ऐसा संस्करण पैदा हुआ जो केवल कैश-फ्रेंडली सीड ब्लॉक बनाने के लिए दोहरीकरण का उपयोग करता है, जो तब लक्ष्य लक्ष्य पर चलने के लिए नष्ट हो जाता है:
const int ARRAY_COPY_THRESHOLD = 32; // 16 ... 64 work equally well for all tested constellations
const int L1_CACHE_SIZE = 1 << 15;
public static void Fill<T> (T[] array, int count, T value, int element_size)
{
int current_size = 0, keep_looping_up_to = Math.Min(count, ARRAY_COPY_THRESHOLD);
while (current_size < keep_looping_up_to)
array[current_size++] = value;
int block_size = L1_CACHE_SIZE / element_size / 2;
int keep_doubling_up_to = Math.Min(block_size, count >> 1);
for ( ; current_size < keep_doubling_up_to; current_size <<= 1)
Array.Copy(array, 0, array, current_size, current_size);
for (int enough = count - block_size; current_size < enough; current_size += block_size)
Array.Copy(array, 0, array, current_size, block_size);
Array.Copy(array, 0, array, current_size, count - current_size);
}
नोट: (count + 1) >> 1
अंतिम कोड दोहरीकरण लूप के लिए सीमा के रूप में आवश्यक है ताकि यह सुनिश्चित किया जा सके कि अंतिम कॉपी ऑपरेशन में शेष सभी को कवर करने के लिए पर्याप्त चारा है। इसके count >> 1
बजाय यदि इसका उपयोग किया जाना हो तो यह अजीब बात नहीं है । वर्तमान संस्करण के लिए इसका कोई महत्व नहीं है क्योंकि लीनियर कॉपी लूप किसी भी सुस्त को उठाएगा।
एक सरणी सेल का आकार एक पैरामीटर के रूप में पारित किया जाना चाहिए क्योंकि - दिमाग चकराता है - जेनरिक का उपयोग करने की अनुमति नहीं है sizeof
जब तक कि वे एक बाधा ( unmanaged
) का उपयोग नहीं करते हैं जो भविष्य में उपलब्ध हो सकता है या नहीं हो सकता है। गलत अनुमान कोई बड़ी बात नहीं है, लेकिन प्रदर्शन सबसे अच्छा है यदि मूल्य सटीक है, तो निम्न कारणों से:
तत्व आकार को कम आंकने से आधे L1 कैश से अधिक आकार ब्लॉक हो सकते हैं, इसलिए प्रतिलिपि स्रोत डेटा की L1 से बेदखल होने और धीमी कैश स्तरों से फिर से प्राप्त होने की संभावना बढ़ जाती है।
सीपीयू के एल 1 कैश के अंडर-उपयोग में तत्व के आकार के परिणामों को कम करके, जिसका अर्थ है कि रैखिक ब्लॉक कॉपी लूप को अधिक से अधिक बार निष्पादित किया जाता है, यह इष्टतम उपयोग के साथ होगा। इस प्रकार, निश्चित लूप / कॉल ओवरहेड का अधिक कड़ाई से आवश्यक है।
यहाँ एक बेंचमार्क मेरे कोड के खिलाफ Array.Clear
और अन्य तीन समाधानों का उल्लेख किया गया है। समय Int32[]
दिए गए आकारों के पूर्णांक सरणियों ( ) को भरने के लिए हैं। कैश योनि आदि के कारण भिन्नता को कम करने के लिए, प्रत्येक परीक्षण को दो बार, बैक-टू-बैक निष्पादित किया गया था, और दूसरे निष्पादन के लिए समय लिया गया था।
array size Array.Clear Eric J. Panos Theof Petar Petrov Darth Gizka
-------------------------------------------------------------------------------
1000: 0,7 µs 0,2 µs 0,2 µs 6,8 µs 0,2 µs
10000: 8,0 µs 1,4 µs 1,2 µs 7,8 µs 0,9 µs
100000: 72,4 µs 12,4 µs 8,2 µs 33,6 µs 7,5 µs
1000000: 652,9 µs 135,8 µs 101,6 µs 197,7 µs 71,6 µs
10000000: 7182,6 µs 4174,9 µs 5193,3 µs 3691,5 µs 1658,1 µs
100000000: 67142,3 µs 44853,3 µs 51372,5 µs 35195,5 µs 16585,1 µs
क्या इस कोड का प्रदर्शन पर्याप्त नहीं होना चाहिए, तो एक होनहार एवेन्यू रैखिक कॉपी लूप (समान स्रोत ब्लॉक का उपयोग करने वाले सभी थ्रेड्स के साथ), या हमारे अच्छे पुराने दोस्त P / Invoke को समानांतर करेगा।
ध्यान दें: क्लीयरिंग और ब्लॉक्स को सामान्य रूप से रनटाइम रूटीन द्वारा किया जाता है जो कि MMX / SSE निर्देशों और व्हाट्सएप का उपयोग करते हुए अत्यधिक विशिष्ट कोड की शाखा होती है, इसलिए किसी भी सभ्य वातावरण में एक व्यक्ति संबंधित संबंधित नैतिक स्तर को समान रूप से कॉल करेगा std::memset
और पेशेवर प्रदर्शन स्तरों का आश्वासन दिया जाएगा। IOW, लाइब्रेरी फंक्शन द्वारा अधिकारों को Array.Clear
हमारे सभी हाथ से लुढ़का हुआ संस्करण धूल में छोड़ देना चाहिए। तथ्य यह है कि यह चारों ओर का दूसरा रास्ता दिखाता है कि वास्तव में अजीब चीजें कितनी दूर हैं। वही Fill<>
पहली बार किसी का रोल करने के लिए जाता है, क्योंकि यह अभी भी कोर और स्टैंडर्ड में ही है लेकिन फ्रेमवर्क में नहीं है। .NET को अभी लगभग बीस साल हो गए हैं और हमें अभी भी सबसे बुनियादी सामान के लिए P / Invoke को बाएं और दाएं करना है या अपना स्वयं का रोल करना है ...