簡體   English   中英

提高將字節轉換為 UInt32 的性能

[英]Improving performance converting bytes into UInt32

我正在研究處理 2GB 數據的源代碼,它代表 60 秒的網絡流量。 總處理時間約為 40 秒。 我正在嘗試盡可能優化我的代碼以獲得最佳性能,以嘗試將總處理時間控制在 30 秒以下。

線程時間

我當前在 dotTrace 中的分析表明,在我的代碼進行的 330 萬次調用中,有 7.62% 的時間花在了 Timestamp 結構的構造函數中。

構造函數

具體來說,我正在嘗試改進兩個陳述:

TimestampHigh = BitConverter.ToUInt32(timestampBytes, 0);
TimestampLow = BitConverter.ToUInt32(timestampBytes, 4);

這是完整的結構:

public readonly struct Timestamp
{
    public uint TimestampHigh { get; }
    public uint TimestampLow { get; }
    public uint Seconds { get; }
    public uint Microseconds { get; }
    public DateTime LocalTime => new DateTime(EpochTicks + _ticks, DateTimeKind.Utc).ToLocalTime();

    private const ulong MicrosecondsPerSecond = 1000000UL;
    private const ulong HighFactor = 4294967296UL;
    private readonly ulong _timestamp;

    private const long EpochTicks = 621355968000000000L;
    private const long TicksPerMicrosecond = 10L;
    private readonly long _ticks;

    public Timestamp(byte[] timestampBytes, bool reverseByteOrder)
    {
        if (timestampBytes == null)
            throw new ArgumentNullException($"{nameof(timestampBytes)} cannot be null.");
        if (timestampBytes.Length != 8)
            throw new ArgumentException($"{nameof(timestampBytes)} must have a length of 8.");

        TimestampHigh = BitConverter.ToUInt32(timestampBytes, 0).ReverseByteOrder(reverseByteOrder);
        TimestampLow = BitConverter.ToUInt32(timestampBytes, 4).ReverseByteOrder(reverseByteOrder);
        _timestamp = ((ulong)TimestampHigh * HighFactor) + (ulong)TimestampLow;
        _ticks = (long)_timestamp * TicksPerMicrosecond;
        Seconds = (uint)(_timestamp / MicrosecondsPerSecond);
        Microseconds = (uint)(_timestamp % MicrosecondsPerSecond);
    }

    public Timestamp(uint seconds, uint microseconds)
    {
        Seconds = seconds;
        Microseconds = microseconds;
        _timestamp = seconds * MicrosecondsPerSecond + microseconds;
        _ticks = (long)_timestamp * TicksPerMicrosecond;
        TimestampHigh = (uint)(_timestamp / HighFactor);
        TimestampLow = (uint)(_timestamp % HighFactor);
    }

    public byte[] ConvertToBytes(bool reverseByteOrder)
    {
        List<byte> bytes = new List<byte>();
        bytes.AddRange(BitConverter.GetBytes(TimestampHigh.ReverseByteOrder(reverseByteOrder)));
        bytes.AddRange(BitConverter.GetBytes(TimestampLow.ReverseByteOrder(reverseByteOrder)));

        return bytes.ToArray();
    }

    public bool Equals(Timestamp other)
    {
        return TimestampLow == other.TimestampLow && TimestampHigh == other.TimestampHigh;
    }

    public static bool operator ==(Timestamp left, Timestamp right)
    {
        return left.Equals(right);
    }

    public static bool operator !=(Timestamp left, Timestamp right)
    {
        return !left.Equals(right);
    }

    public override bool Equals(object obj)
    {
        return obj is Timestamp other && Equals(other);
    }

    public override int GetHashCode()
    {
        return _timestamp.GetHashCode();
    }
}

根據 dotTrace,ReverseByteOrder 方法似乎不會帶來太多性能損失,因為它代表的時間不到 0.5%,但此處僅供參考:

public static UInt32 ReverseByteOrder(this UInt32 value, bool reverseByteOrder)
{
    if (!reverseByteOrder)
    {
        return value;
    }
    else
    {
        byte[] bytes = BitConverter.GetBytes(value);
        Array.Reverse(bytes);
        return BitConverter.ToUInt32(bytes, 0);
    }
}

看起來你正在做很多工作來對抗字節序。 老實說,這就是BitConverter的落腳點。 好消息是,在現代運行時我們有BinaryPrimitives ,它具有字節序感知操作,然后是 JIT 優化的。 含義:編寫時檢查了 CPU-endianness,但在 JIT 期間該檢查被刪除,只保留與 CPU 相關的代碼。 所以:避免BitConverter 確實需要在您的代碼中進行一些修改,因為reverseByteOrder不再是input ,但請考慮:

(注意:您可以將byte[]作為Span<byte> / ReadOnlySpan<byte>傳遞 - 它是隱式的)

public Timestamp(ReadOnlySpan<byte> timestampBytes)
{
    static void ThrowInvalidLength() // can help inlining in some useful cases
            => throw new ArgumentException($"{nameof(timestampBytes)} must have a length of 8.");
    if (timestampBytes.Length != 8) ThrowInvalidLength();

     TimestampHigh = BinaryPrimitives.ReadUInt32BigEndian(timestampBytes);
     TimestampLow = BinaryPrimitives.ReadUInt32BigEndian(timestampBytes.Slice(4));
    // ...
}

public void ConvertToBytes(Span<byte> destination)
{
    BinaryPrimitives.WriteUInt32BigEndian(destination, TimestampHigh);
    BinaryPrimitives.WriteUInt32BigEndian(destination.Slice(4), TimestampLow);
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM