有效地将字节数组转换为十进制 [英] Efficiently convert byte array to Decimal

查看:249
本文介绍了有效地将字节数组转换为十进制的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

If I have a byte array and want to convert a contiguous 16 byte block of that array, containing .net's representation of a Decimal, into a proper Decimal struct, what is the most efficient way to do it?

以下是我的探查器中显示为最大CPU的代码

Here's the code that showed up in my profiler as the biggest CPU consumer in a case that I'm optimizing.

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();
    }
}

摆脱 MemoryStream BinaryReader ,我认为应该提供一个 BitConverter.ToInt32(src,offset + x) s放入 Decimal(Int32 [])构造函数将比我在下面介绍的解决方案要快,但是奇怪的是,下面的版本要快两倍。 p>

To get rid of MemoryStream and BinaryReader, I thought feeding an array of BitConverter.ToInt32(src, offset + x)s into the Decimal(Int32[]) constructor would be faster than the solution I present below, but the version below is, strangely enough, twice as fast.

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]);
}

这是 10倍 code> MemoryStream / BinaryReader 组合,我用一堆极限值对其进行了测试以确保它可以工作,但是十进制表示不像其他原始类型那么简单,所以我尚未确信它可用于100%的可能十进制值。

This is 10 times as fast as the MemoryStream/BinaryReader combo, and I tested it with a bunch of extreme values to make sure it works, but the decimal representation is not as straightforward as that of other primitive types, so I'm not yet convinced it works for 100% of the possible decimal values.

但是,从理论上讲,可能有一种方法可以将这16个连续字节复制到其他地方在内存中并声明为十进制,而无需任何检查。

In theory however, there could be a way to copy those 16 contiguous byte to some other place in memory and declare that to be a Decimal, without any checks. Is anyone aware of a method to do this?

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

(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...)

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

Or is there any other faster way?

推荐答案

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

Even though this is an old question, I was a bit intrigued, so decided to run some experiments. Let's start with the experiment code.

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();
}

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

Result: Time: 1.68s serialization, 1.81s deserialization. This is our baseline. I also tried Buffer.BlockCopy to an int[4], which gives us 0.42s for deserialization. Using the method described in the question, deserialization goes down to 0.29s.


但是,从理论上讲,有一种方法可以将这16个连续的
字节复制到内存中的其他位置并声明成为小数,
,不作任何检查。有没有人知道这样做的方法?

In theory however, there could be a way to copy those 16 contiguous byte to some other place in memory and declare that to be a Decimal, without any checks. Is anyone aware of a method to do this?

是的,最快的方法是使用不安全的代码,即好的,因为小数是值类型:

Well yes, the fastest way to do this is to use unsafe code, which is okay here because decimals are value types:

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();
}

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

At this point, our result is: Time: 0.07s serialization, 0.16s deserialization. Pretty sure that's the fastest this is going to get... still, you have to accept unsafe here, and I assume stuff is written the same way as it's read.

这篇关于有效地将字节数组转换为十进制的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆