【问题标题】:Optimize bit reader for ReadInt on datastream优化数据流上 ReadInt 的位读取器
【发布时间】:2013-12-18 03:55:12
【问题描述】:

谁能帮我优化这段代码?它目前是一个很大的瓶颈,因为它经常被调用。即使是 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 完成的基准测试。目前这段代码占用了大约 15% 的总 cpu 时间。最少的数字赢得最佳答案。

编辑:示例用法:

          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 字节;

【问题讨论】:

  • 您至少可以提供一些示例输入吗? Data 有多大?您在调用此方法时提供了什么 length
  • ...另外,Length 属性呢?
  • 您期待什么样的输出?或者至少解释一下你在做什么。
  • 已更新。长度可以是 8、9 或 32

标签: c# stream streaming


【解决方案1】:

如何使用指针和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 方法。希望 cmets 对解决方案有所了解:

    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},正如预期的那样。

【讨论】:

  • 这个对我不起作用。一些使用示例 Field0 = buffer.ReadInt(9); ProtocolHash = buffer.ReadInt(32); Field1 = buffer.ReadInt(8);
  • 哇,这个例子真是有点……它仍然没有说什么!我 100% 确信使用指针会比您的解决方案快得多,但除非您提出您的问题属性,使用正确的代码和使用示例,您不会得到比我的答案更多的东西。
  • 太棒了。你的更新什么也没说。我可以读到该方法从它的声明中获取int。它是否显示了有关如何创建作为方法参数传递的buffer 变量的任何信息? BitBuffer 类的外观如何? DataPositionLength 的属性有哪些类型?任何示例输入+输出组合?
  • var packet = (BitBuffer)incomingBuffer.ParsePacket();数据包 ParsePacket() { var pack = this.Parse();退货包; } BitBuffer 有这 3 个字段和其他不相关的东西。
  • 在这里粘贴 3 页代码没有意义。我只是强调了热点。除非您想重新设计整个实现,否则您所要求的无关紧要,这超出了此问题的范围。
【解决方案2】:

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

不要在循环内创建新的 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;
}

希望这能稍微提高你的表现

【讨论】:

  • +0:我不相信您的示例代码和原始代码之间有任何区别 - 在每个循环与循环外声明变量应该没有任何惩罚,因为编译器 + JIT 应该注意它...显示实际数字会使答案有用。
  • 经过测试,编译器只是在循环内声明整数,无论​​是调试还是发布版本。
猜你喜欢
  • 1970-01-01
  • 2020-10-20
  • 2019-12-28
  • 2020-11-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-05-14
  • 1970-01-01
相关资源
最近更新 更多