复制系统.Guid '到' byte[] '没有分配

本文关键字:分配 系统 Guid 复制 byte | 更新日期: 2023-09-27 18:18:57

我正在开发的应用程序非常关注性能,因此需要将分配保持在最低限度,以降低GC延迟。

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

所以我要找的是某种方式来复制一个Guid到一个已经存在的byte[]缓冲区没有分配任何内存(因为Guid是一个值类型已经)。

复制系统.Guid '到' byte[] '没有分配

我确定的解决方案来自于Kevin Montrose的Jil项目的一些帮助。我没有采用那个精确的解决方案,但它激发了我的灵感,让我想到了一些我认为相当优雅的东西。

注意:以下代码使用固定大小缓冲区,并要求您的项目使用/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];
        }
    }
}

用法很简单:

var myGuid = Guid.NewGuid(); // however you get it
var guidBuffer = new GuidBuffer(myGuid);
var buffer = new buffer[16];
guidBuffer.CopyTo(buffer, 0);

计时产生1-2 滴答副本的平均持续时间。对于大多数应用程序来说应该足够快了。

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

如果速度是主要考虑因素,可以通过直接使用Guid而不是通过GuidBuffer结构体来节省大量时间。这是我使用的扩展方法。

public static unsafe void Encode(this byte[] array, int offset, Guid value)
{
    if (array.Length - offset < 16) throw new ArgumentException("buffer too small");
    fixed (byte* pArray = array)
    {
        var pGuid = (long*)&value;
        var pDest = (long*)(pArray + offset);
        pDest[0] = pGuid[0];
        pDest[1] = pGuid[1];
    }
}

用法:

var guid  = Guid.NewGuid();
var array = new byte[16];
array.Encode(0, guid);