提高 Bitconverter.ToInt16 的性能
Improve performance of Bitconverter.ToInt16
我正在从 USB 设备收集数据,这些数据必须转到音频输出组件。目前,我传送数据的速度不够快,无法避免输出信号中出现咔哒声。所以每一毫秒都很重要。
目前我正在收集以 65536 bytes.The 字节数组形式传送的数据,前两个字节代表 小端 格式的 16 位数据。这两个字节必须放在双精度数组的第一个元素中。后两个字节必须放在不同双精度数组的第一个元素中。然后对 65536 缓冲区中的所有字节重复此操作,以便最终得到 2 double[]
个大小为 16384 的数组。
我目前正在使用 BitConverter.ToInt16,如代码所示。 运行 这需要大约 0.3 毫秒,但必须完成 10 次才能将数据包发送到音频输出。所以开销是 3 毫秒,这足以让一些数据包最终无法按时交付。
代码
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);
}
我该如何改进?是否可以使用不安全代码复制值?我没有这方面的经验。
谢谢
使用 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 % |
-----------------------------------------------------------------------------
"So every millisecond counts." 如果是这样的话,你在这里处理的是 Realtime Programming。尽管 .NET 运行时功能强大,但对于实时编程来说并不理想。
单独的垃圾收集内存管理通常是 disqualifier for Realtime Programming。
现在您可以将 .NET 从 GC 内存管理更改为直接管理。并通过转到不安全代码和使用裸指针来挤出一点性能。但这几乎就是您删除 .NET 的所有卖点的地方。最好先用本机 C++ 编写整个 thing/that 部分。
我正在从 USB 设备收集数据,这些数据必须转到音频输出组件。目前,我传送数据的速度不够快,无法避免输出信号中出现咔哒声。所以每一毫秒都很重要。
目前我正在收集以 65536 bytes.The 字节数组形式传送的数据,前两个字节代表 小端 格式的 16 位数据。这两个字节必须放在双精度数组的第一个元素中。后两个字节必须放在不同双精度数组的第一个元素中。然后对 65536 缓冲区中的所有字节重复此操作,以便最终得到 2 double[]
个大小为 16384 的数组。
我目前正在使用 BitConverter.ToInt16,如代码所示。 运行 这需要大约 0.3 毫秒,但必须完成 10 次才能将数据包发送到音频输出。所以开销是 3 毫秒,这足以让一些数据包最终无法按时交付。
代码
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);
}
我该如何改进?是否可以使用不安全代码复制值?我没有这方面的经验。 谢谢
使用 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 % |
-----------------------------------------------------------------------------
"So every millisecond counts." 如果是这样的话,你在这里处理的是 Realtime Programming。尽管 .NET 运行时功能强大,但对于实时编程来说并不理想。
单独的垃圾收集内存管理通常是 disqualifier for Realtime Programming。
现在您可以将 .NET 从 GC 内存管理更改为直接管理。并通过转到不安全代码和使用裸指针来挤出一点性能。但这几乎就是您删除 .NET 的所有卖点的地方。最好先用本机 C++ 编写整个 thing/that 部分。