3

我需要制作一个 byte[] -> T 扩展方法并且需要它快速(不需要它很漂亮)

在绝对性能关键的环境中,此功能将在非常短的时间内连续调用 100 次或 1000 次。

我们目前正在优化“滴答声”级别,每个滴答声在调用堆栈中转换为高几毫秒,因此需要原始速度而不是可维护性(不是我喜欢设计软件的方式,但其背后的原因超出了范围) .

考虑以下代码,它干净且可维护,但速度相对较慢(可能是由于装箱和拆箱),是否可以将其优化为更快?

public static T ConvertTo<T>(this byte[] bytes, int offset = 0)
{
    var type = typeof(T);
    if (type == typeof(sbyte)) return bytes[offset].As<T>();
    if (type == typeof(byte)) return bytes[offset].As<T>();
    if (type == typeof(short)) return BitConverter.ToInt16(bytes, offset).As<T>();
    if (type == typeof(ushort)) return BitConverter.ToUInt32(bytes, offset).As<T>();
    if (type == typeof(int)) return BitConverter.ToInt32(bytes, offset).As<T>();
    if (type == typeof(uint)) return BitConverter.ToUInt32(bytes, offset).As<T>();
    if (type == typeof(long)) return BitConverter.ToInt64(bytes, offset).As<T>();
    if (type == typeof(ulong)) return BitConverter.ToUInt64(bytes, offset).As<T>();

    throw new NotImplementedException();
}

public static T As<T>(this object o)
{
    return (T)o;
}
4

2 回答 2

6

我也需要这个,我发现使用指针和不安全的代码可以更快地转换到这些原始类型。喜欢:

public unsafe int ToInt(byte[] bytes, int offset)
{
    fixed(byte* ptr = bytes)
    {
         return *(int*)(ptr + offset);
    }
}

但不幸的是,C# 不支持泛型指针类型,所以我不得不在 IL 中编写这种和平的代码,它不太关心泛型约束:

.method public hidebysig static !!T  Read<T>(void* ptr) cil managed
{
    .maxstack  8
    nop         
    ldarg.0     
    ldobj !!T
    ret 
}

这并不漂亮,但它似乎对我有用。使用此方法时要小心 - 您可以将任何类型作为参数传递,但我不知道它会做什么。

您可以在github上找到整个 il 文件 或下载已编译的程序集https://github.com/exyi/RaptorDB-Document/blob/master/GenericPointerHelpers/GenericPointerHelpers.dll?raw=true我那里还有几个助手。

编辑:我忘了写如何使用这个方法。假设您已经像我一样在类中编译了该方法,GenericPointerHelper您可以实现ConvertTo类似于我的方法ToInt

public unsafe T ConvertTo<T>(byte[] bytes, int offset)
    where T: struct // not needed to work, just to eliminate some errors
{
    fixed(byte* ptr = bytes)
    {
         return GenericPointerHelper.Read<T>(ptr + offset);
    }
}
于 2015-08-22T09:51:46.457 回答
1

没有什么能比exyi提供的解决方案的性能更好。我对unsafe代码没有任何问题,但是相应的BitConverter类方法(http://referencesource.microsoft.com/#mscorlib/system/bitconverter.cs)中的某些东西真的让我很担心——它们会进行对齐检查,并对未对齐的情况使用不同的实现。下面是一个更安全的纯 C# 解决方案 - 它可能有点慢(但应该测量,你永远不知道),但应该比原来的快得多。作为奖励,我添加了显式名称方法(类似于 中的那些BitConverter)除了使用bytes.To<int>()(这还可以,但有点奇怪),您可以使用更方便bytes.ToInt32()(应该比通用方法更快)。

public static class BitConverter<T> where T : struct
{
    public static readonly Func<byte[], int, T> To = GetFunc();
    static Func<byte[], int, T> GetFunc()
    {
        var type = typeof(T);
        if (type == typeof(bool)) return Cast(BitConverter.ToBoolean);
        if (type == typeof(sbyte)) return Cast(Extensions.ToSByte);
        if (type == typeof(byte)) return Cast(Extensions.ToByte);
        if (type == typeof(short)) return Cast(BitConverter.ToInt16);
        if (type == typeof(ushort)) return Cast(BitConverter.ToUInt16);
        if (type == typeof(int)) return Cast(BitConverter.ToInt32);
        if (type == typeof(uint)) return Cast(BitConverter.ToUInt32);
        if (type == typeof(long)) return Cast(BitConverter.ToInt64);
        if (type == typeof(ulong)) return Cast(BitConverter.ToUInt64);
        if (type == typeof(float)) return Cast(BitConverter.ToSingle);
        if (type == typeof(double)) return Cast(BitConverter.ToDouble);
        throw new NotSupportedException();
    }
    static Func<byte[], int, T> Cast<U>(Func<byte[], int, U> func) { return (Func<byte[], int, T>)(object)func; }
}

public static class Extensions
{
    public static bool ToBoolean(this byte[] bytes, int offset = 0) { return BitConverter.ToBoolean(bytes, offset); }
    public static sbyte ToSByte(this byte[] bytes, int offset = 0) { return unchecked((sbyte)bytes[offset]); }
    public static byte ToByte(this byte[] bytes, int offset = 0) { return bytes[offset]; }
    public static short ToInt16(this byte[] bytes, int offset = 0) { return BitConverter.ToInt16(bytes, offset); }
    public static ushort ToUInt16(this byte[] bytes, int offset = 0) { return BitConverter.ToUInt16(bytes, offset); }
    public static int ToInt32(this byte[] bytes, int offset = 0) { return BitConverter.ToInt32(bytes, offset); }
    public static uint ToUInt32(this byte[] bytes, int offset = 0) { return BitConverter.ToUInt32(bytes, offset); }
    public static long ToInt64(this byte[] bytes, int offset = 0) { return BitConverter.ToInt64(bytes, offset); }
    public static ulong ToUInt64(this byte[] bytes, int offset = 0) { return BitConverter.ToUInt64(bytes, offset); }
    public static float ToSingle(this byte[] bytes, int offset = 0) { return BitConverter.ToSingle(bytes, offset); }
    public static double ToDouble(this byte[] bytes, int offset = 0) { return BitConverter.ToDouble(bytes, offset); }
    public static T To<T>(this byte[] bytes, int offset = 0) where T : struct { return BitConverter<T>.To(bytes, offset); }
}
于 2015-08-23T19:00:11.207 回答