簡體   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