繁体   English   中英

有效地将字节数组转换为十进制

[英]Efficiently convert byte array to Decimal

如果我有一个字节数组,并且想要将该数组的连续的16字节块(包含.net的Decimal表示形式)转换为适当的Decimal结构,最有效的方法是什么?

在我正在优化的情况下,这是我的探查器中显示为最大CPU使用者的代码。

public static decimal ByteArrayToDecimal(byte[] src, int offset)
{
    using (MemoryStream stream = new MemoryStream(src))
    {
        stream.Position = offset;
        using (BinaryReader reader = new BinaryReader(stream))
            return reader.ReadDecimal();
    }
}

为了摆脱MemoryStreamBinaryReader ,我认为将BitConverter.ToInt32(src, offset + x) s数组输入Decimal(Int32[])构造函数将比我在下面提供的解决方案要快,但是下面的版本是:奇怪的是,速度快一倍。

const byte DecimalSignBit = 128;
public static decimal ByteArrayToDecimal(byte[] src, int offset)
{
    return new decimal(
        BitConverter.ToInt32(src, offset),
        BitConverter.ToInt32(src, offset + 4),
        BitConverter.ToInt32(src, offset + 8),
        src[offset + 15] == DecimalSignBit,
        src[offset + 14]);
}

这是MemoryStream/BinaryReader组合速度的10倍 ,我用一堆极限值对其进行了测试以确保它可以正常工作,但是十进制表示并不像其他原始类型那样简单,因此我还没有确信它适用于100%的可能十进制值。

但是,从理论上讲,有一种方法可以将这16个连续字节复制到内存中的其他位置,并声明它为Decimal,而无需进行任何检查。 有人知道这样做的方法吗?

(只有一个问题:尽管小数表示为16个字节,但是某些可能的值不能构成有效的小数,因此,执行未经检查的memcpy可能会破坏内容...)

还是还有其他更快的方法?

从流中读取@Eugene Beresovksy非常昂贵。 MemoryStream当然是功能强大且用途广泛的工具,但是直接读取二进制数组的成本却很高。 也许正因为如此,第二种方法的性能更好。

我为您提供了第三个解决方案,但是在我编写它之前,有必要说我还没有测试它的性能。

public static decimal ByteArrayToDecimal(byte[] src, int offset)
{
    var i1 = BitConverter.ToInt32(src, offset);
    var i2 = BitConverter.ToInt32(src, offset + 4);
    var i3 = BitConverter.ToInt32(src, offset + 8);
    var i4 = BitConverter.ToInt32(src, offset + 12);

    return new decimal(new int[] { i1, i2, i3, i4 });
}

这是一种使构建基于二进制文件而不用担心System.Decimal规范的方法。 它是默认.net位提取方法的逆过程:

System.Int32[] bits = Decimal.GetBits((decimal)10);

编辑:

此解决方案可能不会表现更好,但也不会出现此问题: "(There's only one problem: Although decimals are represented as 16 bytes, some of the possible values do not constitute valid decimals, so doing an uncheckedmemcpy could potentially break things...)"

尽管这是一个古老的问题,但我还是很感兴趣,因此决定进行一些实验。 让我们从实验代码开始。

static void Main(string[] args)
{
    byte[] serialized = new byte[16 * 10000000];

    Stopwatch sw = Stopwatch.StartNew();
    for (int i = 0; i < 10000000; ++i)
    {
        decimal d = i;

        // Serialize
        using (var ms = new MemoryStream(serialized))
        {
            ms.Position = (i * 16);
            using (var bw = new BinaryWriter(ms))
            {
                bw.Write(d);
            }
        }
    }
    var ser = sw.Elapsed.TotalSeconds;

    sw = Stopwatch.StartNew();
    decimal total = 0;
    for (int i = 0; i < 10000000; ++i)
    {
        // Deserialize
        using (var ms = new MemoryStream(serialized))
        {
            ms.Position = (i * 16);
            using (var br = new BinaryReader(ms))
            {
                total += br.ReadDecimal();
            }
        }
    }
    var dser = sw.Elapsed.TotalSeconds;

    Console.WriteLine("Time: {0:0.00}s serialization, {1:0.00}s deserialization", ser, dser);
    Console.ReadLine();
}

结果: Time: 1.68s serialization, 1.81s deserialization 这是我们的基准。 我还尝试了Buffer.BlockCopyint[4] ,这给了我们0.42s的反序列化时间。 使用问题中描述的方法,反序列化可降至0.29s。

但是,从理论上讲,有一种方法可以将这16个连续字节复制到内存中的其他位置,并声明它为Decimal,而无需进行任何检查。 有人知道这样做的方法吗?

嗯,是的,最快的方法是使用不安全的代码,这在这里还可以,因为小数是值类型:

static unsafe void Main(string[] args)
{
    byte[] serialized = new byte[16 * 10000000];

    Stopwatch sw = Stopwatch.StartNew();
    for (int i = 0; i < 10000000; ++i)
    {
        decimal d = i;

        fixed (byte* sp = serialized)
        {
            *(decimal*)(sp + i * 16) = d;
        }
    }
    var ser = sw.Elapsed.TotalSeconds;

    sw = Stopwatch.StartNew();
    decimal total = 0;
    for (int i = 0; i < 10000000; ++i)
    {
        // Deserialize
        decimal d;
        fixed (byte* sp = serialized)
        {
            d = *(decimal*)(sp + i * 16);
        }

        total += d;
    }
    var dser = sw.Elapsed.TotalSeconds;

    Console.WriteLine("Time: {0:0.00}s serialization, {1:0.00}s deserialization", ser, dser);

    Console.ReadLine();
}

此时,我们的结果是: Time: 0.07s serialization, 0.16s deserialization 可以肯定的是,这将是最快的速度...仍然,您必须在这里接受不安全的信息,并且我假设内容的编写方式与读取的方式相同。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM