优化数据流上ReadInt的位读取器

时间:2013-12-18 03:55:12

标签: c# stream streaming

有人可以帮我优化这段代码吗?它目前是一个很大的瓶颈,因为它经常被调用。即使速度提高25%也是如此。

public int ReadInt(int length)
{
    if (Position + length > Length)
        throw new BitBufferException("Not enough bits remaining.");

    int result = 0;
    while (length > 0)
    {
        int off = Position & 7;
        int count = 8 - off;
        if (count > length)
            count = length;
        int mask = (1 << count) - 1;
        int bits = (Data[Position >> 3] >> off);
        result |= (bits & mask) << (length - count);
        length -= count;
        Position += count;
    }
    return result;
}

最佳答案将是最快的解决方案。使用dottrace完成的基准测试。目前,这段代码占用了总CPU时间的15%左右。最低的数字赢得最佳答案。

编辑:样本用法:

          public class Auth : Packet
          {
            int Field0;
            int ProtocolHash;
            int Field1;

            public override void Parse(buffer)
            {
            Field0 = buffer.ReadInt(9);
            ProtocolHash = buffer.ReadInt(32);
            Field1 = buffer.ReadInt(8);
            }
          }

数据大小是可变的,但在大多数情况下是512字节;

2 个答案:

答案 0 :(得分:2)

如何使用指针和unsafe上下文?您没有对输入数据,方法上下文等进行任何说明,所以我试图自己推断所有这些。

public class BitTest
{
    private int[] _data;

    public BitTest(int[] data)
    {
        Length = data.Length * 4 * 8;

        // +2, because we use byte* and long* later
        // and don't want to read outside the array memory
        _data = new int[data.Length + 2];
        Array.Copy(data, _data, data.Length);
    }

    public int Position { get; private set; }
    public int Length { get; private set; }

ReadInt方法。希望评论对解决方案有所启发:

    public unsafe int ReadInt(int length)
    {
        if (Position + length > Length)
            throw new ArgumentException("Not enough bits remaining.");

        // method returns int, so getting more then 32 bits is pointless
        if (length > 4 * 8)
            throw new ArgumentException();

        // 
        int bytePosition = Position / 8;
        int bitPosition = Position % 8;
        Position += length;

        // get int* on array to start with
        fixed (int* array = _data)
        {
            // change pointer to byte*
            byte* bt = (byte*)array;
            // skip already read bytes and change pointer type to long*
            long* ptr = (long*)(bt + bytePosition);
            // read value from current pointer position
            long value = *ptr;

            // take only necessary bits
            value &= (1L << (length + bitPosition)) - 1;
            value >>= bitPosition;

            // cast value to int before returning
            return (int)value;
        }
    }
}

我没有测试方法,但是打赌它比你的方法快得多。

我的简单测试代码:

var data = new[] { 1 | (1 << 8 + 1) | (1 << 16 + 2) | (1 << 24 + 3) };
var test = new BitTest(data);

var bytes = Enumerable.Range(0, 4)
                      .Select(x => test.ReadInt(8))
                      .ToArray();
正如预期的那样,

bytes包含{ 1, 2, 4, 8}

答案 1 :(得分:1)

我不知道这是否会给你带来重大改进,但它应该给你一些数字。

不是在循环内创建新的int变量(这需要时间来创建),而是在进入循环之前保留这些变量。

public int ReadInt(int length)
{
if (Position + length > Length)
    throw new BitBufferException("Not enough bits remaining.");

int result = 0;
int off = 0;
int count = 0;
int mask = 0;
int bits = 0
while (length > 0)
{
    off = Position & 7;
    count  = 8 - off;
    if (count > length)
        count = length;
    mask = (1 << count) - 1;
    bits = (Data[Position >> 3] >> off);
    result |= (bits & mask) << (length - count);
    length -= count;
    Position += count;
}
return result;
}

希望这可以提高你的表现