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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1897555/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 21:43:24  来源:igfitidea点击:

What is the equivalent of memset in C#?

c#memsetequivalent

提问by Jedidja

I need to fill a byte[]with a single non-zerovalue. How can I do this in C# without looping through each bytein the array?

我需要byte[]用一个非零值填充 a 。如何在 C# 中执行此操作而不遍历byte数组中的每个?

Update:The comments seem to have split this into two questions -

更新:评论似乎将其分为两个问题-

  1. Is there a Framework method to fill a byte[] that might be akin to memset
  2. What is the most efficient way to do it when we are dealing with a very large array?
  1. 是否有一个框架方法来填充一个字节 [] 可能类似于 memset
  2. 当我们处理一个非常大的数组时,最有效的方法是什么?

I totally agree that using a simple loop works just fine, as Eric and others have pointed out. The point of the question was to see if I could learn something new about C# :) I think Juliet's method for a Parallel operation should be even faster than a simple loop.

正如 Eric 和其他人指出的那样,我完全同意使用简单的循环效果很好。问题的重点是看看我是否能学到一些关于 C# 的新知识 :) 我认为 Juliet 的 Parallel 操作方法应该比简单的循环更快。

Benchmarks:Thanks to Mikael Svenson: http://techmikael.blogspot.com/2009/12/filling-array-with-default-value.html

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

It turns out the simple forloop is the way to go unless you want to use unsafe code.

事实证明,for除非您想使用不安全的代码,否则简单循环是可行的方法。

Apologies for not being clearer in my original post. Eric and Mark are both correct in their comments; need to have more focused questions for sure. Thanks for everyone's suggestions and responses.

抱歉在我原来的帖子中没有说清楚。Eric 和 Mark 的评论都是正确的;肯定需要有更集中的问题。谢谢大家的建议和回复。

回答by Mark Byers

You could use Enumerable.Repeat:

你可以使用Enumerable.Repeat

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

The first parameter is the element you want repeated, and the second parameter is the number of times to repeat it.

第一个参数是你想要重复的元素,第二个参数是重复的次数。

This is OK for small arrays but you should use the looping method if you are dealing with very large arrays and performance is a concern.

这对于小数组来说是可以的,但是如果您正在处理非常大的数组并且性能是一个问题,则应该使用循环方法。

回答by Cory Charlton

You could do it when you initialize the array but I don't think that's what you are asking for:

您可以在初始化数组时执行此操作,但我认为这不是您的要求:

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

回答by Jan

If performance is critical, you could consider using unsafe code and working directly with a pointer to the array.

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

Another option could be importing memset from msvcrt.dll and use that. However, the overhead from invoking that might easily be larger than the gain in speed.

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

回答by Juliet

If performance is absolutely critical, then Enumerable.Repeat(n, m).ToArray()will be too slow for your needs. You might be able to crank out faster performance using PLINQ or Task Parallel Library:

如果性能绝对至关重要,那么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);

回答by Lucero

A little bit late, but the following approach might be a good compromise without reverting to unsafe code. Basically it initializes the beginning of the array using a conventional loop and then reverts to Buffer.BlockCopy(), which should be as fast as you can get using a managed call.

有点晚了,但以下方法可能是一个很好的折衷方案,而不会恢复到不安全的代码。基本上它使用传统循环初始化数组的开头,然后恢复到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;
  }
}

回答by TowerOfBricks

Building on Lucero's answer, here is a faster version. It will double the number of bytes copied using Buffer.BlockCopyevery iteration. Interestingly enough, it outperforms it by a factor of 10 when using relatively small arrays (1000), but the difference is not that large for larger arrays (1000000), it is always faster though. The good thing about it is that it performs well even down to small arrays. It becomes faster than the naive approach at around length = 100. For a one million element byte array, it was 43 times faster. (tested on Intel i7, .Net 2.0)

基于Lucero's answer,这里有一个更快的版本。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;
    }
}

回答by staafl

This simple implementation uses successive doubling, and performs quite well (about 3-4 times faster than the naive version according to my benchmarks):

这个简单的实现使用连续加倍,并且性能非常好(根据我的基准测试,比原始版本快大约 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);
}

Edit: upon reading the other answers, it seems I'm not the only one with this idea. Still, I'm leaving this here, since it's a bit cleaner and it performs on par with the others.

编辑:在阅读其他答案时,似乎我不是唯一一个有这个想法的人。尽管如此,我还是把它留在这里,因为它更干净一点,而且性能与其他产品不相上下。

回答by Agnius Vasiliauskas

Or use P/Invoke way:

或者使用 P/Invoke 方式

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

回答by konrad.kruczynski

Actually, there is little known IL operation called Initblk(English version) which does exactly that. So, let's use it as a method that doesn't require "unsafe". Here's the helper class:

实际上,鲜为人知的称为Initblk英文版)的IL 操作正是这样做的。因此,让我们将其用作不需要“不安全”的方法。这是帮助类:

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;

}

And what is the performance? Here's my result for Windows/.NET and Linux/Mono (different PCs).

性能如何?这是我对 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

So it's worth considering. Note that the resulting IL will not be verifiable.

所以值得考虑。请注意,生成的 IL 将无法验证。

回答by Eric

All answers are writing single bytes only - what if you want to fill a byte array with words? Or floats? I find use for that now and then. So after having written similar code to 'memset' in a non-generic way a few times and arriving at this page to find good code for single bytes, I went about writing the method below.

所有答案都只写入单个字节 - 如果您想用单词填充字节数组怎么办?还是漂浮?我偶尔会发现它的用处。因此,在以非通用方式编写了几次类似的“memset”代码并到达此页面以找到单字节的好代码之后,我开始编写下面的方法。

I think PInvoke and C++/CLI each have their drawbacks. And why not have the runtime 'PInvoke' for you into mscorxxx? Array.Copy and Buffer.BlockCopy are native code certainly. BlockCopy isn't even 'safe' - you can copy a long halfway over another, or over a DateTime as long as they're in arrays.

我认为 PInvoke 和 C++/CLI 各有其缺点。为什么不在 mscorxxx 中为您提供运行时“PInvoke”?Array.Copy 和 Buffer.BlockCopy 肯定是本地代码。BlockCopy 甚至不是“安全的”——只要它们在数组中,您就可以在另一个或 DateTime 的中途复制很长的一段。

At least I wouldn't go file new C++ project for things like this - it's a waste of time almost certainly.

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

So here's basically an extended version of the solutions presented by Lucero and TowerOfBricks that can be used to memset longs, ints, etc as well as single bytes.

所以这里基本上是 Lucero 和 TowerOfBricks 提供的解决方案的扩展版本,可用于 memset longs、ints 等以及单个字节。

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

Having this you can simply add short methods to take the value type you need to memset with and call the private method, e.g. just find replace ulong in this method:

有了这个,您可以简单地添加简短的方法来获取您需要使用 memset 的值类型并调用私有方法,例如只需在此方法中找到替换 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);
    }

Or go silly and do it with any type of struct (although the MemsetPrivate above only works for structs that marshal to a size that is a power of two):

或者愚蠢地使用任何类型的结构(尽管上面的 MemsetPrivate 仅适用于编组为 2 的幂的大小的结构):

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

I changed the initblk mentioned before to take ulongs to compare performance with my code and that silently fails - the code runs but the resulting buffer contains the least significant byte of the ulong only.

我更改了之前提到的 initblk 以使用 ulong 将性能与我的代码进行比较,并且默默地失败了 - 代码运行但结果缓冲区仅包含 ulong 的最低有效字节。

Nevertheless I compared the performance writing as big a buffer with for, initblk and my memset method. The times are in ms total over 100 repetitions writing 8 byte ulongs whatever how many times fit the buffer length. The for version is manually loop-unrolled for the 8 bytes of a single ulong.

尽管如此,我还是将写入作为大缓冲区的性能与 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

I excluded the first call every time, since both initblk and memset take a hit of I believe it was about .22ms for the first call. Slightly surprising my code is faster for filling short buffers than initblk, seeing it got half a page full of setup code.

我每次都排除了第一次调用,因为 initblk 和 memset 都受到了影响,我相信第一次调用大约是 0.22 毫秒。有点令人惊讶的是,我的代码填充短缓冲区的速度比 initblk 快,因为它有半页的设置代码。

If anybody feels like optimizing this, go ahead really. It's possible.

如果有人想对此进行优化,请继续。这是可能的。