所有答案都只写单个字节 - 如果你想用单词填充字节数组怎么办?还是漂浮?我时不时地找到它的用途。因此,在以非通用方式多次编写了与“memset”类似的代码并到达此页面以找到适合单字节的代码之后,我开始编写以下方法。
我认为 PInvoke 和 C++/CLI 各有其缺点。为什么不在 mscorxxx 中为您提供运行时“PInvoke”?Array.Copy 和 Buffer.BlockCopy 当然是原生代码。BlockCopy 甚至不是“安全的”——只要它们在数组中,您就可以在另一个或 DateTime 上复制很长的一半。
至少我不会为这样的事情提交新的 C++ 项目——这几乎可以肯定是浪费时间。
所以这里基本上是 Lucero 和 TowerOfBricks 提出的解决方案的扩展版本,可用于 memset long、int 等以及单字节。
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);
}
}
有了这个,你可以简单地添加简短的方法来获取你需要的值类型并调用私有方法,例如在这个方法中找到替换 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 只适用于编组为 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);
}
我更改了前面提到的 initblk 以使用 ulongs 来比较我的代码的性能,并且默默地失败了 - 代码运行但生成的缓冲区仅包含 ulong 的最低有效字节。
尽管如此,我还是将性能写入作为大缓冲区与 for、initblk 和我的 memset 方法进行了比较。时间以毫秒为单位,总共超过 100 次重复写入 8 字节 ulong,无论多少次适合缓冲区长度。对于单个 ulong 的 8 个字节,for 版本是手动循环展开的。
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 都受到了打击,我相信第一次通话大约是 0.22 毫秒。有点令人惊讶的是,我的代码填充短缓冲区比 initblk 更快,因为它有半页的设置代码。
如果有人想优化这个,那就继续吧。这是可能的。