代码之家  ›  专栏  ›  技术社区  ›  Jedidja

C#中的memset的等价物是什么?

  •  73
  • Jedidja  · 技术社区  · 16 年前

    我需要填补一个空缺 byte[] 带着一个 非零 byte

    更新: 这些评论似乎把这分成了两个问题-

    1. 是否有框架方法来填充可能类似于 memset

    正如Eric和其他人指出的,我完全同意使用一个简单的循环可以很好地工作。问题的关键是看我是否能学到一些关于C的新知识:)我认为Juliet的并行操作方法应该比简单的循环更快。

    基准: 感谢Mikael Svenson: http://techmikael.blogspot.com/2009/12/filling-array-with-default-value.html

    for 循环是一种方法,除非您想使用不安全的代码。

    很抱歉在我原来的帖子中没有说得更清楚。埃里克和马克的评论都是正确的;当然,我们需要有更集中的问题。谢谢大家的建议和回复。

    14 回复  |  直到 10 年前
        1
  •  64
  •   Mark Byers    16 年前

    Enumerable.Repeat :

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

    这对于小型阵列是可以的,但是如果您处理的是非常大的阵列,并且性能是一个问题,那么应该使用循环方法。

        2
  •  52
  •   Rob K    10 年前

    实际上,很少有人知道IL操作叫做 Initblk English version )正是这样。因此,让我们将其用作一种不需要“不安全”的方法。以下是helper类:

    public static class Util
    {
        static Util()
        {
            var dynamicMethod = new DynamicMethod("Memset", MethodAttributes.Public | MethodAttributes.Static, CallingConventions.Standard,
                null, new [] { typeof(IntPtr), typeof(byte), typeof(int) }, typeof(Util), true);
    
            var generator = dynamicMethod.GetILGenerator();
            generator.Emit(OpCodes.Ldarg_0);
            generator.Emit(OpCodes.Ldarg_1);
            generator.Emit(OpCodes.Ldarg_2);
            generator.Emit(OpCodes.Initblk);
            generator.Emit(OpCodes.Ret);
    
            MemsetDelegate = (Action<IntPtr, byte, int>)dynamicMethod.CreateDelegate(typeof(Action<IntPtr, byte, int>));
        }
    
        public static void Memset(byte[] array, byte what, int length)
        {
            var gcHandle = GCHandle.Alloc(array, GCHandleType.Pinned);
            MemsetDelegate(gcHandle.AddrOfPinnedObject(), what, length);
            gcHandle.Free();
        }
    
        public static void ForMemset(byte[] array, byte what, int length)
        {
            for(var i = 0; i < length; i++)
            {
                array[i] = what;
            }
        }
    
        private static Action<IntPtr, byte, int> MemsetDelegate;
    
    }
    

    表演怎么样?下面是我对Windows/.NET和Linux/Mono(不同的PC)的结果。

    Mono/for:     00:00:01.1356610
    Mono/initblk: 00:00:00.2385835 
    
    .NET/for:     00:00:01.7463579
    .NET/initblk: 00:00:00.5953503
    

    所以这是值得考虑的。请注意,生成的IL将不可验证。

        3
  •  23
  •   Community Mohan Dere    9 年前

    建立在 Lucero's answer ,这是一个更快的版本。它将使用复制的字节数增加一倍 Buffer.BlockCopy (在英特尔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;
        }
    }
    
        4
  •  22
  •   Lucero    16 年前

    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;
      }
    }
    
        5
  •  13
  •   staafl    12 年前

    这个简单的实现使用了连续加倍,并且执行得非常好(根据我的基准测试,大约比naive版本快3-4倍):

    public static void Memset<T>(T[] array, T elem) 
    {
        int length = array.Length;
        if (length == 0) return;
        array[0] = elem;
        int count;
        for (count = 1; count <= length/2; count*=2)
            Array.Copy(array, 0, array, count, count);
        Array.Copy(array, 0, array, count, length - count);
    }
    

    编辑:在阅读其他答案后,似乎我不是唯一一个有这个想法的人。尽管如此,我还是把它留在这里,因为它有点干净,而且和其他人一样。

        6
  •  12
  •   Jan    16 年前

    如果性能很重要,您可以考虑使用不安全代码并直接使用指向数组的指针。

    另一个选项是从msvcrt.dll导入memset并使用它。然而,调用它的开销可能很容易大于速度上的增益。

        7
  •  12
  •   Gman    8 年前

    System.Runtime.CompilerServices.Unsafe.InitBlock 现在做的事情和 OpCodes.Initblk source link ).

    要填充数组的代码如下所示:

    byte[] a = new byte[N];
    byte valueToFill = 255;
    
    System.Runtime.CompilerServices.Unsafe.InitBlock(ref a[0], valueToFill, (uint) a.Length);
    
        8
  •  9
  •   Agnius Vasiliauskas    12 年前

    Or use P/Invoke way :

    [DllImport("msvcrt.dll", 
    EntryPoint = "memset", 
    CallingConvention = CallingConvention.Cdecl, 
    SetLastError = false)]
    public static extern IntPtr MemSet(IntPtr dest, int c, int count);
    
    static void Main(string[] args)
    {
        byte[] arr = new byte[3];
        GCHandle gch = GCHandle.Alloc(arr, GCHandleType.Pinned);
        MemSet(gch.AddrOfPinnedObject(), 0x7, arr.Length); 
    }
    
        9
  •  8
  •   Lucero    12 年前

    如果性能是绝对关键的,那么 Enumerable.Repeat(n, m).ToArray() 对你的需求来说,速度太慢了。您可以使用PLINQ或 Task Parallel Library :

    using System.Threading.Tasks;
    
    // ...
    
    byte initialValue = 20;
    byte[] data = new byte[size]
    Parallel.For(0, size, index => data[index] = initialValue);
    
        10
  •  6
  •   Eric    11 年前

    所有答案都只写单个字节-如果要用单词填充字节数组怎么办?还是浮动?我时不时地发现它的用处。因此,在以非泛型的方式编写了几次类似于“memset”的代码并在本页找到了单字节的好代码之后,我开始编写下面的方法。

    我认为PInvoke和C++/CLI各有缺点。为什么不让运行时“PInvoke”进入mscorxxx?Array.Copy和Buffer.BlockCopy当然是本机代码。BlockCopy甚至不是“安全的”-您可以复制一个长文件,复制到另一个文件的一半,或者复制到一个日期时间,只要它们在数组中。

    至少我不会为这样的事情提交新的C++项目——这几乎是浪费时间。

    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);
        }
    }
    

    有了此功能,您只需添加简短的方法,以获取需要使用的值类型并调用私有方法,例如,只需在此方法中查找replace ulong:

        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只适用于封送到二次幂大小的结构):

        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);
        }
    

    尽管如此,我还是将性能写入与for、initblk和memset方法进行了比较。时间以毫秒为单位,超过100次重复写入8字节Ulong,无论多少次符合缓冲区长度。for版本是针对单个ulong的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
    

    如果有人想优化这个,那就真的去吧。这是可能的。

        11
  •  4
  •   constructor    10 年前

    测试了几种不同答案中描述的方法。 参见c中的测试来源# test class

    benchmark report

        12
  •  3
  •   Cory Charlton    16 年前

    byte[] myBytes = new byte[5] { 1, 1, 1, 1, 1};
    
        13
  •  2
  •   Rook    6 年前

    随着 Span<T> (这只是dotnet核心,但 it is the future of dotnet

    var array = new byte[100];
    var span = new Span<byte>(array);
    
    span.Fill(255);
    
        14
  •  1
  •   DragonSpit    7 年前

    .NET Core有一个内置的Array.Fill()函数,但遗憾的是.NET Framework缺少它。NETCORE有两种变体:填充整个数组和从索引开始填充数组的一部分。

    在上述思想的基础上,这里有一个更通用的Fill函数,它将填充多个数据类型的整个数组。与本文讨论的其他方法进行基准测试时,这是最快的功能。

    此函数以及填充阵列一部分的版本在开源和免费的NuGet包中提供( HPCsharp on nuget.org ). 还包括使用SIMD/SSE指令的稍快版本的Fill,该指令仅执行内存写入,而基于块拷贝的方法执行内存读取和写入。

        public static void FillUsingBlockCopy<T>(this T[] array, T value) where T : struct
        {
            int numBytesInItem = 0;
            if (typeof(T) == typeof(byte) || typeof(T) == typeof(sbyte))
                numBytesInItem = 1;
            else if (typeof(T) == typeof(ushort) || typeof(T) != typeof(short))
                numBytesInItem = 2;
            else if (typeof(T) == typeof(uint) || typeof(T) != typeof(int))
                numBytesInItem = 4;
            else if (typeof(T) == typeof(ulong) || typeof(T) != typeof(long))
                numBytesInItem = 8;
            else
                throw new ArgumentException(string.Format("Type '{0}' is unsupported.", typeof(T).ToString()));
    
            int block = 32, index = 0;
            int endIndex = Math.Min(block, array.Length);
    
            while (index < endIndex)          // Fill the initial block
                array[index++] = value;
    
            endIndex = array.Length;
            for (; index < endIndex; index += block, block *= 2)
            {
                int actualBlockSize = Math.Min(block, endIndex - index);
                Buffer.BlockCopy(array, 0, array, index * numBytesInItem, actualBlockSize * numBytesInItem);
            }
        }
    
        15
  •  0
  •   sajjad    5 年前

    大多数答案都是针对byte memset的,但若要将其用于float或任何其他结构,则应将index乘以数据的大小。因为Buffer.BlockCopy将基于字节进行复制。 此代码适用于浮点值

    public static void MemSet(float[] array, float 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 * sizeof(float), Math.Min(block, length-index)* sizeof(float));
            index += block;
            block *= 2;
        }
    }
    
        16
  •  -1
  •   Robert Columbia yusuf dalal    8 年前

    数组对象有一个名为Clear的方法。我敢打赌Clear方法比用C#编写的任何代码都要快。

    推荐文章