代码之家  ›  专栏  ›  技术社区  ›  Tom

提高bitconverter.toint16的性能

  •  5
  • Tom  · 技术社区  · 7 年前

    我正在从USB设备收集数据,这些数据必须转到音频输出组件。目前,我传递数据的速度不够快,无法避免在输出信号中发出咔哒声。所以每毫秒都很重要。

    目前我正在收集以65536字节的字节数组传送的数据。前两个字节代表 小字节 格式。这两个字节必须放在双数组的第一个元素中。第二个两字节必须放在不同的双数组的第一个元素中。然后对65536缓冲区中的所有字节重复此操作,这样最终得到2 double[] 16384大小的数组。

    如代码所示,我目前正在使用bitconverter.toint16。运行这个程序大约需要0.3ms,但要想让一个数据包发送到音频输出,必须执行10次。所以开销是3ms,这刚好足够让一些包最终不能按时交付。

    代码

    byte[] buffer = new byte[65536];
    double[] bufferA = new double[16384];
    double[] bufferB = new double[16384]
    
    for(int i= 0; i < 65536; i +=4)
    {
        bufferA[i/4] = BitConverter.ToInt16(buffer, i);
        bufferB[i/4] = BitConverter.ToInt16(buffer, i+2);
    }
    

    我该如何改进?是否可以复制带有不安全代码的值?我没有这方面的经验。 谢谢

    2 回复  |  直到 7 年前
        1
  •  4
  •   TheGeneral    7 年前

    这使我的释放速度增加了三倍,使用 指针 unsafe . 也许还有其他的微观优化,不过我会把这些细节留给大众。

    更新的

    我原来的算法有一个错误,可以改进

    修改代码

    public unsafe (double[], double[]) Test2(byte[] input, int scale)
    {
       var bufferA = new double[input.Length / 4];
       var bufferB = new double[input.Length / 4];
    
       fixed (byte* pSource = input)
          fixed (double* pBufferA = bufferA, pBufferB = bufferB)
          {
             var pLen = pSource + input.Length;
             double* pA = pBufferA, pB = pBufferB;
    
             for (var pS = pSource; pS < pLen; pS += 4, pA++, pB++)
             {
                *pA = *(short*)pS;
                *pB = *(short*)(pS + 2);
             }
          }
    
       return (bufferA, bufferB);
    }
    

    贝恩马克

    每个测试运行1000次,每次运行前收集垃圾,并缩放到不同的数组长度。所有结果均与原始操作版本进行核对。

    测试环境

    ----------------------------------------------------------------------------
    Mode             : Release (64Bit)
    Test Framework   : .NET Framework 4.7.1 (CLR 4.0.30319.42000)
    ----------------------------------------------------------------------------
    Operating System : Microsoft Windows 10 Pro
    Version          : 10.0.17134
    ----------------------------------------------------------------------------
    CPU Name         : Intel(R) Core(TM) i7-3770K CPU @ 3.50GHz
    Description      : Intel64 Family 6 Model 58 Stepping 9
    Cores (Threads)  : 4 (8)      : Architecture  : x64
    Clock Speed      : 3901 MHz   : Bus Speed     : 100 MHz
    L2Cache          : 1 MB       : L3Cache       : 8 MB
    ----------------------------------------------------------------------------
    

    结果

    --- Random Set of byte ------------------------------------------------------
    | Value    |    Average |    Fastest |    Cycles | Garbage | Test |    Gain |
    --- Scale 16,384 -------------------------------------------- Time 13.727 ---
    | Unsafe   |  19.487 µs |  14.029 µs |  71.479 K | 0.000 B | Pass | 59.02 % |
    | Original |  47.556 µs |  34.781 µs | 169.580 K | 0.000 B | Base |  0.00 % |
    --- Scale 32,768 -------------------------------------------- Time 14.809 ---
    | Unsafe   |  40.398 µs |  31.274 µs | 145.024 K | 0.000 B | Pass | 56.62 % |
    | Original |  93.127 µs |  79.501 µs | 329.320 K | 0.000 B | Base |  0.00 % |
    --- Scale 65,536 -------------------------------------------- Time 18.984 ---
    | Unsafe   |  68.318 µs |  43.550 µs | 245.083 K | 0.000 B | Pass | 68.34 % |
    | Original | 215.758 µs | 160.171 µs | 758.955 K | 0.000 B | Base |  0.00 % |
    --- Scale 131,072 ------------------------------------------- Time 22.620 ---
    | Unsafe   | 120.764 µs |  79.208 µs | 428.626 K | 0.000 B | Pass | 71.24 % |
    | Original | 419.889 µs | 322.388 µs |   1.461 M | 0.000 B | Base |  0.00 % |
    -----------------------------------------------------------------------------
    
        2
  •  -1
  •   Christopher    7 年前

    “所以每毫秒都很重要。”如果是这样的话,你就要处理 Realtime Programming 在这里。尽管如此,.NET运行时并不是实时编程的理想选择。

    垃圾收集内存管理通常是 disqualifier for Realtime Programming .

    现在您可以将.NET从GC内存管理更改为直接管理。通过使用不安全的代码和裸指针来挤出一点性能。但这几乎就是你删除.NET所有卖点的关键所在。最好先写一个完整的东西/那个部分在本地C++中。