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

时间:2015-10-22 23:03:29

标签: c# memory optimization

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

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

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

2 个答案:

答案 0 :(得分:3)

我确定的解决方案来自Jil project Kevin Montrose的一些帮助。我没有采用那种确切的解决方案,但它激发了我想出一些我认为相当优雅的东西。

注意:以下代码使用Fixed Size Buffers,并要求使用/unsafe开关构建项目(并且很可能需要运行Full Trust)。

[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 ticks 。对于大多数应用来说应该足够快。

但是,如果你想要实现绝对的最佳性能,一种可能性(凯文建议)是确保offset参数为long - 对齐(在8字节边界上)。我的特定用例有利于内存超速,但如果速度是最重要的,那将是一个很好的方法。

答案 1 :(得分:1)

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

public static unsafe void Encode(this byte[] array, int offset, Guid value)
{
    if (array.Length - offset < 16) 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);