是否有比BitConverter.ToInt32
更快的方法将字节数组转换为int值?
答案 0 :(得分:18)
我实际上尝试了几种不同的方法将四个字节转换为int:
BitConverter.ToInt32(new byte[] { w, x, y, z }, 0);
BitConverter.ToUInt32(new byte[] { w, x, y, z }, 0);
b = new byte[] { w, x, y, z };
BitConverter.ToInt32(b, 0);
b = new byte[] { 1, 2, 3, 4, 5, 6, 7, w, x, y, z };
BitConverter.ToInt32(b, 7);
w | (x << 8) | (y << 16) | (z << 24);
b[0] | (b[1] << 8) | (b[2] << 16) | (b[3] << 24);
我在Release(x86)版本中运行了10 ^ 9次迭代,而不是在2.5 GHz Core i7笔记本电脑上的调试器下运行。以下是我的结果(请注意,不使用BitConverter
的方法要快得多):
test1: 00:00:15.5287282 67305985
test2: 00:00:15.1334457 67305985
test3: 00:00:08.0648586 67305985
test4: 00:00:11.2307059 67305985
test5: 00:00:02.0219417 67305985
test6: 00:00:01.6275684 67305985
您可以得出一些结论:
uint
代替int
可以节省少量时间。我不确定为什么,但我认为它足够小,可以成为实验性错误。ToInt32
进行未对齐的数组访问会增加开销(3ns)ToInt32
快几倍。最快的test6只用了两倍的空循环(未显示)。换句话说,执行每次转换所需的时间不到1ns。祝你好运,获得任何有用的计算都要比这更快!
这是我的测试程序:
using System;
namespace BitConverterTest
{
class Program
{
const int iters = 1000000000;
static void Main(string[] args)
{
test1(1, 2, 3, 4);
test2(1, 2, 3, 4);
test3(1, 2, 3, 4);
test4(1, 2, 3, 4);
test5(1, 2, 3, 4);
test6(1, 2, 3, 4);
}
static void test1(byte w, byte x, byte y, byte z)
{
int res = 0;
var timer = System.Diagnostics.Stopwatch.StartNew();
for (int i = 0; i < iters; i++)
res = BitConverter.ToInt32(new byte[] { w, x, y, z }, 0);
Console.WriteLine("test1: " + timer.Elapsed + " " + res);
}
static void test2(byte w, byte x, byte y, byte z)
{
uint res = 0;
var timer = System.Diagnostics.Stopwatch.StartNew();
for (int i = 0; i < iters; i++)
res = BitConverter.ToUInt32(new byte[] { w, x, y, z }, 0);
Console.WriteLine("test2: " + timer.Elapsed + " " + res);
}
static void test3(byte w, byte x, byte y, byte z)
{
int res = 0;
var timer = System.Diagnostics.Stopwatch.StartNew();
var b = new byte[] { w, x, y, z };
for (int i = 0; i < iters; i++)
res = BitConverter.ToInt32(b, 0);
Console.WriteLine("test3: " + timer.Elapsed + " " + res);
}
static void test4(byte w, byte x, byte y, byte z)
{
int res = 0;
var timer = System.Diagnostics.Stopwatch.StartNew();
var b = new byte[] { 1, 2, 3, 4, 5, 6, 7, w, x, y, z };
for (int i = 0; i < iters; i++)
res = BitConverter.ToInt32(b, 7);
Console.WriteLine("test4: " + timer.Elapsed + " " + res);
}
static void test5(byte w, byte x, byte y, byte z)
{
int res = 0;
var timer = System.Diagnostics.Stopwatch.StartNew();
var b = new byte[] { w, x, y, z };
for (int i = 0; i < iters; i++)
res = w | (x << 8) | (y << 16) | (z << 24);
Console.WriteLine("test5: " + timer.Elapsed + " " + res);
}
static void test6(byte w, byte x, byte y, byte z)
{
int res = 0;
var timer = System.Diagnostics.Stopwatch.StartNew();
var b = new byte[] { w, x, y, z };
for (int i = 0; i < iters; i++)
res = b[0] | (b[1] << 8) | (b[2] << 16) | (b[3] << 24);
Console.WriteLine("test6: " + timer.Elapsed + " " + res);
}
}
}
答案 1 :(得分:6)
如果我没记错的话,那个实现使用了不安全的代码(将一个字节*视为一个int *),所以它很难被击败,但另一种选择是转移。
然而,从这个领域的很多的工作来看,这不太可能是一个真正的瓶颈,无关紧要。 I / O通常是主要问题。
GetBytes(int),但由于数组/堆分配, 更昂贵(大批量)。
答案 2 :(得分:4)
跟进Gabe's性能测试:
的变化:
32位选项:
test3: 00:00:06.9230577
test5: 00:00:03.8349386
test6: 00:00:03.8238272
test7: 00:00:07.3898489
test8: 00:00:04.6807391
64位选项:
test3: 00:00:05.8794322
test5: 00:00:00.4384600
test6: 00:00:00.4069573
test7: 00:00:06.2279365
test8: 00:00:03.5472486
BitConverter
开销?using System;
namespace BitConverterTest {
class Program {
const int iters = 1024*1024*1024;
const int arrayLen = iters/4;
static byte[] array = new byte[arrayLen];
static void Main(string[] args) {
//test1(1, 2, 3, 4);
//test2(1, 2, 3, 4);
test3(1, 2, 3, 4);
//test4(1, 2, 3, 4);
test5(1, 2, 3, 4);
test6(1, 2, 3, 4);
// Fill array with good PRNG data
var rng = new System.Security.Cryptography.RNGCryptoServiceProvider();
rng.GetBytes(array);
test7();
test8();
}
// BitConverter with aligned input
static void test3(byte w, byte x, byte y, byte z) {
int res = 0;
var timer = System.Diagnostics.Stopwatch.StartNew();
var b = new byte[] { w, x, y, z };
for (int i = 0; i < iters; i++)
res = BitConverter.ToInt32(b, 0);
Console.WriteLine("test3: " + timer.Elapsed + " " + res);
}
// Inline bitfiddling with separate variables.
static void test5(byte w, byte x, byte y, byte z) {
long res = 0;
var timer = System.Diagnostics.Stopwatch.StartNew();
var b = new byte[] { w, x, y, z };
for (int i = 0; i < iters; i++) {
int a = w | (x << 8) | (y << 16) | (z << 24);
res += a;
}
Console.WriteLine("test5: " + timer.Elapsed + " " + res);
}
// Inline bitfiddling with array elements.
static void test6(byte w, byte x, byte y, byte z) {
long res = 0;
var timer = System.Diagnostics.Stopwatch.StartNew();
var b = new byte[] { w, x, y, z };
for (int i = 0; i < iters; i++) {
int a = b[0] | (b[1] << 8) | (b[2] << 16) | (b[3] << 24);
res += a;
}
Console.WriteLine("test6: " + timer.Elapsed + " " + res);
}
// BitConvert from large array...
static void test7() {
var its = iters/arrayLen * 4; // *4 to remove arrayLen/4 factor.
var timer = System.Diagnostics.Stopwatch.StartNew();
long res = 0;
for (var outer = 0; outer < its; outer++) {
for (var pos = 0; pos < arrayLen; pos += 4) {
var x = BitConverter.ToInt32(array, pos);
res += x;
}
}
Console.WriteLine("test7: " + timer.Elapsed + " " + res);
}
// Bitfiddle from large array...
static void test8() {
var its = iters/arrayLen * 4;
var timer = System.Diagnostics.Stopwatch.StartNew();
long res = 0;
for (var outer = 0; outer < its; outer++) {
for (var pos = 0; pos < arrayLen; pos += 4) {
int x = array[pos] | (array[pos+1] << 8) | (array[pos+2] << 16) | (array[pos+3] << 24);
res += x;
}
}
Console.WriteLine("test8: " + timer.Elapsed + " " + res);
}
}
}
答案 3 :(得分:3)
基于对.NET Reflector中BitConverter.ToInt32
实现的快速回顾,我会说“否”。
它优化了数组对齐并直接转换字节的情况,否则它执行按位合并。
答案 4 :(得分:0)
我也摆弄了类似的问题。
在我的情况下,当数据存储为双精度float
时,或仅在byte[]
表示和{{1}之间时,它是如何转换为单精度double
的如果想要在大型数据集上获得最佳性能,并尽可能多地将信息嵌入到算法中而不会使其过于脆弱或难以理解,那么最好不要经历过多的API层。
因此,为了进一步跟进Richard's测试,我在下面添加了另一个测试(byte[]
),这是我在自己的工作中的方式,并在他的分析部分回答了他的观点4 :
使用不安全的内存指针访问来获得最高效的结果。 如果你使用c ++,那就很自然了,但不一定是c#。这类似于BitConverter在幕后所做的事情,但没有参数和安全检查(当然,我们知道我们在做什么......;)
结果:
32位选项:
test9
64位选项:
test3: 00:00:06.2373138
test5: 00:00:03.1193338
test6: 00:00:03.1609287
test7: 00:00:07.7328020
test8: 00:00:06.4192130
test9: 00:00:03.9590307
这里是相同的代码,包括新的test3: 00:00:06.2209098
test5: 00:00:00.5563930
test6: 00:00:01.5486780
test7: 00:00:08.4858474
test8: 00:00:05.4991740
test9: 00:00:02.2928944
:
test9