c# - सी#में मेमसेट के बराबर क्या है?




memset equivalent (9)

आप Enumerable.Repeat उपयोग कर सकते हैं। Enumerable.Repeat :

byte[] a = Enumerable.Repeat((byte)10, 100).ToArray();

पहला पैरामीटर वह तत्व है जिसे आप दोहराया जाना चाहते हैं, और दूसरा पैरामीटर इसे दोहराने के लिए कई बार है।

यह छोटे सरणी के लिए ठीक है लेकिन यदि आप बहुत बड़े सरणी से निपट रहे हैं और प्रदर्शन एक चिंता है तो आपको लूपिंग विधि का उपयोग करना चाहिए।

मुझे एक byte[] को एक गैर-शून्य मान के साथ भरना होगा। सरणी में प्रत्येक byte माध्यम से लूपिंग किए बिना मैं इसे सी # में कैसे कर सकता हूं?

अद्यतन: टिप्पणियां इसे दो प्रश्नों में विभाजित कर रही हैं -

  1. क्या बाइट [] को भरने के लिए एक फ्रेमवर्क विधि है जो memset समान हो सकती है
  2. जब हम एक बहुत बड़ी सरणी से निपट रहे हैं तो ऐसा करने का सबसे प्रभावी तरीका क्या है?

मैं पूरी तरह से सहमत हूं कि एक सरल पाश का उपयोग ठीक काम करता है, क्योंकि एरिक और अन्य ने ध्यान दिया है। सवाल का मुद्दा यह देखना था कि क्या मैं सी # के बारे में कुछ नया सीख सकता हूं :) मुझे लगता है कि समांतर ऑपरेशन के लिए जूलियट की विधि एक साधारण लूप से भी तेज होनी चाहिए।

बेंचमार्क: माइकल स्वेन्सन के लिए धन्यवाद: http://techmikael.blogspot.com/2009/12/filling-array-with-default-value.html

यह पता चला है कि लूप के for सरल तरीका है जब तक आप असुरक्षित कोड का उपयोग नहीं करना चाहते हैं।

मेरी मूल पोस्ट में स्पष्ट नहीं होने के लिए माफ़ी। एरिक और मार्क दोनों अपनी टिप्पणियों में सही हैं; निश्चित रूप से अधिक केंद्रित प्रश्नों की आवश्यकता है। सभी के सुझावों और प्रतिक्रियाओं के लिए धन्यवाद।


ऐरे ऑब्जेक्ट में साफ़ नामक एक विधि है। मैं यह शर्त लगाने के लिए तैयार हूं कि साफ़ विधि किसी भी कोड से तेज़ है जिसे आप सी # में लिख सकते हैं।


जब आप सरणी शुरू करते हैं तो आप इसे कर सकते हैं लेकिन मुझे नहीं लगता कि आप यही पूछ रहे हैं:

byte[] myBytes = new byte[5] { 1, 1, 1, 1, 1};

थोड़ा देर हो चुकी है, लेकिन असुरक्षित कोड को वापस किए बिना निम्न दृष्टिकोण एक अच्छा समझौता हो सकता है। असल में यह एक पारंपरिक लूप का उपयोग करके सरणी की शुरुआत शुरू करता है और फिर Buffer.BlockCopy() वापस जाता है, जो कि एक प्रबंधित कॉल का उपयोग करके आप जितनी जल्दी हो सके उतना तेज़ होना चाहिए।

public static void MemSet(byte[] array, byte value) {
  if (array == null) {
    throw new ArgumentNullException("array");
  }
  const int blockSize = 4096; // bigger may be better to a certain extent
  int index = 0;
  int length = Math.Min(blockSize, array.Length);
  while (index < length) {
    array[index++] = value;
  }
  length = array.Length;
  while (index < length) {
    Buffer.BlockCopy(array, 0, array, index, Math.Min(blockSize, length-index));
    index += blockSize;
  }
}

यदि प्रदर्शन बिल्कुल महत्वपूर्ण है, तो Enumerable.Repeat(n, m).ToArray() आपकी आवश्यकताओं के लिए बहुत धीमा हो जाएगा। आप PLINQ या कार्य समांतर लाइब्रेरी का उपयोग करके तेज़ प्रदर्शन को क्रैंक करने में सक्षम हो सकते हैं:

using System.Threading.Tasks;

// ...

byte initialValue = 20;
byte[] data = new byte[size]
Parallel.For(0, size, index => data[index] = initialValue);

यदि प्रदर्शन महत्वपूर्ण है, तो आप असुरक्षित कोड का उपयोग करने और सरणी में पॉइंटर के साथ सीधे काम करने पर विचार कर सकते हैं।

एक और विकल्प msvcrt.dll से मेमसेट आयात कर सकता है और इसका उपयोग कर सकता है। हालांकि, आक्रमण से ओवरहेड जो गति में लाभ से आसानी से बड़ा हो सकता है।


विभिन्न उत्तरों में वर्णित कई तरीकों का परीक्षण किया। सी # टेस्ट क्लास में परीक्षण के स्रोत देखें


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

मुझे लगता है कि PInvoke और C ++ / CLI प्रत्येक में उनकी कमी है। और mscorxxx में आपके लिए रनटाइम 'PInvoke' क्यों नहीं है? Array.Copy और Buffer.BlockCopy निश्चित रूप से देशी कोड हैं। ब्लॉककॉपी भी 'सुरक्षित' नहीं है - आप एक लंबे समय तक आधे रास्ते की प्रतिलिपि बना सकते हैं, या डेटटाइम पर जब तक कि वे सरणी में हों।

कम से कम मैं इस तरह की चीजों के लिए फ़ाइल नई सी ++ परियोजना नहीं जाऊंगा - यह लगभग निश्चित रूप से समय बर्बाद है।

तो यहां मूल रूप से लुसेरो और टॉवरऑफब्रिक्स द्वारा प्रस्तुत समाधानों का विस्तारित संस्करण है जिसका उपयोग लंबे, इन्स आदि के साथ-साथ एकल बाइट्स को याद करने के लिए किया जा सकता है।

public static class MemsetExtensions
{
    static void MemsetPrivate(this byte[] buffer, byte[] value, int offset, int length) {
        var shift = 0;
        for (; shift < 32; shift++)
            if (value.Length == 1 << shift)
                break;
        if (shift == 32 || value.Length != 1 << shift)
            throw new ArgumentException(
                "The source array must have a length that is a power of two and be shorter than 4GB.", "value");

        int remainder;
        int count = Math.DivRem(length, value.Length, out remainder);

        var si = 0;
        var di = offset;
        int cx;
        if (count < 1) 
            cx = remainder;
        else 
            cx = value.Length;
        Buffer.BlockCopy(value, si, buffer, di, cx);
        if (cx == remainder)
            return;

        var cachetrash = Math.Max(12, shift); // 1 << 12 == 4096
        si = di;
        di += cx;
        var dx = offset + length;
        // doubling up to 1 << cachetrash bytes i.e. 2^12 or value.Length whichever is larger
        for (var al = shift; al <= cachetrash && di + (cx = 1 << al) < dx; al++) {
            Buffer.BlockCopy(buffer, si, buffer, di, cx);
            di += cx;
        }
        // cx bytes as long as it fits
        for (; di + cx <= dx; di += cx)
            Buffer.BlockCopy(buffer, si, buffer, di, cx);
        // tail part if less than cx bytes
        if (di < dx)
            Buffer.BlockCopy(buffer, si, buffer, di, dx - di);
    }
}

ऐसा करने से आप उस विधि प्रकार को लेने के लिए बस छोटी विधियां जोड़ सकते हैं, जिसके लिए आपको याद रखना होगा और निजी विधि को कॉल करना होगा, उदाहरण के लिए बस इस विधि में उलंग को प्रतिस्थापित करें:

    public static void Memset(this byte[] buffer, ulong value, int offset, int count) {
        var sourceArray = BitConverter.GetBytes(value);
        MemsetPrivate(buffer, sourceArray, offset, sizeof(ulong) * count);
    }

या मूर्खतापूर्ण जाओ और इसे किसी भी प्रकार की संरचना के साथ करें (हालांकि ऊपर से MemsetPrivate केवल structs के लिए काम करता है जो आकार की मार्शल दो की शक्ति है):

    public static void Memset<T>(this byte[] buffer, T value, int offset, int count) where T : struct {
        var size = Marshal.SizeOf<T>();
        var ptr = Marshal.AllocHGlobal(size);
        var sourceArray = new byte[size];
        try {
            Marshal.StructureToPtr<T>(value, ptr, false);
            Marshal.Copy(ptr, sourceArray, 0, size);
        } finally {
            Marshal.FreeHGlobal(ptr);
        }
        MemsetPrivate(buffer, sourceArray, offset, count * size);
    }

मैंने अपने कोड के साथ प्रदर्शन की तुलना करने के लिए उलझाने के लिए पहले उल्लिखित इनिटब्लॉक को बदल दिया और वह चुपचाप विफल रहता है - कोड चलता है लेकिन परिणामी बफर में उलंग के कम से कम महत्वपूर्ण बाइट होते हैं।

फिर भी मैंने प्रदर्शन लेखन की तुलना में बड़े, एक inffblk और मेरी यादृच्छिक विधि के साथ एक बफर के रूप में तुलना की। समय एमएस में कुल 100 पुनरावृत्तियां हैं जो 8 बाइट उलझन लिखते हैं जो बफर की लंबाई में कितनी बार फिट होते हैं। संस्करण के लिए एक एकल उलंग के 8 बाइट्स के लिए मैन्युअल रूप से लूप-अनलॉक किया गया है।

Buffer Len  #repeat  For millisec  Initblk millisec   Memset millisec
0x00000008  100      For   0,0032  Initblk   0,0107   Memset   0,0052
0x00000010  100      For   0,0037  Initblk   0,0102   Memset   0,0039
0x00000020  100      For   0,0032  Initblk   0,0106   Memset   0,0050
0x00000040  100      For   0,0053  Initblk   0,0121   Memset   0,0106
0x00000080  100      For   0,0097  Initblk   0,0121   Memset   0,0091
0x00000100  100      For   0,0179  Initblk   0,0122   Memset   0,0102
0x00000200  100      For   0,0384  Initblk   0,0123   Memset   0,0126
0x00000400  100      For   0,0789  Initblk   0,0130   Memset   0,0189
0x00000800  100      For   0,1357  Initblk   0,0153   Memset   0,0170
0x00001000  100      For   0,2811  Initblk   0,0167   Memset   0,0221
0x00002000  100      For   0,5519  Initblk   0,0278   Memset   0,0274
0x00004000  100      For   1,1100  Initblk   0,0329   Memset   0,0383
0x00008000  100      For   2,2332  Initblk   0,0827   Memset   0,0864
0x00010000  100      For   4,4407  Initblk   0,1551   Memset   0,1602
0x00020000  100      For   9,1331  Initblk   0,2768   Memset   0,3044
0x00040000  100      For  18,2497  Initblk   0,5500   Memset   0,5901
0x00080000  100      For  35,8650  Initblk   1,1236   Memset   1,5762
0x00100000  100      For  71,6806  Initblk   2,2836   Memset   3,2323
0x00200000  100      For  77,8086  Initblk   2,1991   Memset   3,0144
0x00400000  100      For 131,2923  Initblk   4,7837   Memset   6,8505
0x00800000  100      For 263,2917  Initblk  16,1354   Memset  33,3719

मैंने हर बार पहली कॉल को बाहर रखा, क्योंकि दोनों initblk और memset एक हिट लेते हैं मेरा मानना ​​है कि यह पहली कॉल के लिए .22ms था। थोड़ा आश्चर्यजनक है कि मेरा कोड initblk की तुलना में छोटे बफर भरने के लिए तेज़ है, क्योंकि इसे सेटअप कोड से भरा आधा पृष्ठ मिला है।

अगर कोई इसे अनुकूलित करने जैसा महसूस करता है, तो वास्तव में आगे बढ़ें। यह संभव है।


लुसेरो के जवाब पर बिल्डिंग, यहां एक तेज संस्करण है। यह प्रत्येक पुनरावृत्ति Buffer.BlockCopy का उपयोग करके कॉपी किए गए बाइट्स की संख्या को दोगुना कर देगा। दिलचस्प बात यह है कि यह अपेक्षाकृत छोटे सरणी (1000) का उपयोग करते समय 10 के कारक से बेहतर प्रदर्शन करता है, लेकिन अंतर बड़ा सरणी (1000000) के लिए बड़ा नहीं है, हालांकि यह हमेशा तेज होता है। इसके बारे में अच्छी बात यह है कि यह छोटे सरणी तक भी अच्छा प्रदर्शन करता है। यह लगभग लंबाई = 100 पर निष्पक्ष दृष्टिकोण से तेज़ हो जाता है। एक मिलियन तत्व बाइट सरणी के लिए, यह 43 गुना तेज था। (इंटेल i7, .NET 2.0 पर परीक्षण किया गया)

public static void MemSet(byte[] array, byte value) {
    if (array == null) {
        throw new ArgumentNullException("array");
    }

    int block = 32, index = 0;
    int length = Math.Min(block, array.Length);

    //Fill the initial array
    while (index < length) {
        array[index++] = value;
    }

    length = array.Length;
    while (index < length) {
        Buffer.BlockCopy(array, 0, array, index, Math.Min(block, length-index));
        index += block;
        block *= 2;
    }
}






equivalent