将“System.Guid”复制到“byte[]”而不分配

ros*_*dia 6 c# memory optimization

我正在开发的应用程序非常注重性能,因此需要将分配保持在最低水平以减少 GC 停顿。

我惊讶地发现System.Guid它没有公开任何将其byte[]表示复制到现有缓冲区的方法。唯一现有的方法Guid.ToByteArray(),执行new byte[]分配,否则没有它就无法获取底层字节。

所以我正在寻找的是某种方式将 a 复制Guid到一个已经存在的byte[]缓冲区而不分配任何内存(因为Guid已经是一个值类型)。

ros*_*dia 5

我确定的解决方案来自Kevin MontroseJil 项目的一些帮助。我没有采用那个确切的解决方案,但它启发我想出了一些我认为相当优雅的解决方案。

注意:以下代码使用固定大小缓冲区,并要求您的项目使用该/unsafe开关构建(并且很可能需要完全信任才能运行)。

[StructLayout(LayoutKind.Explicit)]
unsafe struct GuidBuffer
{
    [FieldOffset(0)]
    fixed long buffer[2];

    [FieldOffset(0)]
    public Guid Guid;

    public GuidBuffer(Guid guid)
        : this()
    {
        Guid = guid;
    }

    public void CopyTo(byte[] dest, int offset)
    {
        if (dest.Length - offset < 16)
            throw new ArgumentException("Destination buffer is too small");

        fixed (byte* bDestRoot = dest)
        fixed (long* bSrc = buffer)
        {
            byte* bDestOffset = bDestRoot + offset;
            long* bDest = (long*)bDestOffset;

            bDest[0] = bSrc[0];
            bDest[1] = bSrc[1];
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

用法很简单:

var myGuid = Guid.NewGuid(); // however you get it
var guidBuffer = new GuidBuffer(myGuid);

var buffer = new buffer[16];
guidBuffer.CopyTo(buffer, 0);
Run Code Online (Sandbox Code Playgroud)

对此进行计时会产生 1-2 个副本的平均持续时间。对于大多数应用程序来说应该足够快。

但是,如果您想获得绝对最佳性能,一种可能性(Kevin 建议)是确保参数offset对齐long(在 8 字节边界上)。我的特定用例更看重内存而不是速度,但如果速度是最重要的事情,那么这将是一个很好的方法。