提高Bitconverter.ToInt16的性能

时间:2018-10-22 02:57:39

标签: c# unsafe-pointers bitconverter

我正在从USB设备收集数据,并且该数据必须发送到音频输出组件。目前,我无法以足够快的速度传递数据,以免在输出信号中产生咔嗒声。因此,每一毫秒都很重要。

此刻,我正在收集以65536个字节的字节数组形式交付的数据。前两个字节表示 little endian 格式的16位数据。这两个字节必须放在double数组的第一个元素中。后两个字节必须放在另一个double数组的第一个元素中。然后,对65536缓冲区中的所有字节重复此操作,从而得到2个大小为16384的double[]数组。

我当前正在使用BitConverter.ToInt16,如代码中所示。运行此程序大约需要0.3毫秒,但必须完成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 个答案:

答案 0 :(得分:4)

使用 Pointers 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次,在每次运行之前收集垃圾,并按比例缩放到各种数组长度。所有结果均根据原始OP版本进行检查

测试环境

----------------------------------------------------------------------------
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 % |
-----------------------------------------------------------------------------

答案 1 :(得分:-1)

“所以每毫秒都是重要的。”如果是这种情况,那么您在这里处理Realtime Programming。尽管功能强大,但.NET Runtime并不是实时编程的理想选择。

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

现在,您可以将.NET从GC内存管理更改为直接管理。通过使用不安全的代码并使用裸露的指针来压缩性能。但这几乎就是删除.NET的每个卖点的关键所在。并且最好是两个都用本机C ++编写整个东西。