重置C int数组为零:最快的方法?

假设我们有T = int,unsigned int,long long int或unsigned long long int的T myarray[100] ,将其所有内容重置为零的最快方法是什么(不仅用于初始化,还用于重置几个内容次在我的程序)? 也许与memset?

对于像T *myarray = new T[100]这样的dynamic数组,同样的问题。

memset (来自<string.h> )可能是最快的标准方式,因为它通常是一个直接写在程序集中并且手工优化的例程。

 memset(myarray, 0, sizeof(myarray)); // for automatically-allocated arrays memset(myarray, 0, N*sizeof(*myarray)); // for heap-allocated arrays, where N is the number of elements 

顺便说一下,在C ++中,惯用的方法是使用std::fill (来自<algorithm> ):

 std::fill(myarray, myarray+N, 0); 

可能会自动优化成一个memset ; 我相当肯定,它会像memset一样快速地处理int ,而如果优化器不够聪明的话,对于较小的types可能会稍微差一些。 不过,如果有疑问,简介。

来自memset()

 memset(myarray, 0, sizeof(myarray)); 

如果sizeof(myarray)的大小在编译时已知,则可以使用sizeof(myarray) 。 否则,如果您使用的是dynamic大小的数组,例如通过mallocnew ,则需要跟踪长度。

这个问题虽然比较古老,但还是需要一些基准的,因为它不是最习惯的方式,也不是最less的方式,而是最快的方式。 没有经过实际testing就回答这个问题是愚蠢的。 所以我比较了四种解决scheme:memset与std :: fill,以及AnT的答案ZERO与使用AVX intrinsics的解决scheme。

请注意,此解决scheme不是通用的,它只适用于32位或64位的数据。 请评论,如果这个代码是做错了什么。

 #include<immintrin.h> #define intrin_ZERO(a,n){\ size_t x = 0;\ const size_t inc = 32 / sizeof(*(a));/*size of 256 bit register over size of variable*/\ for (;x < n-inc;x+=inc)\ _mm256_storeu_ps((float *)((a)+x),_mm256_setzero_ps());\ if(4 == sizeof(*(a))){\ switch(nx){\ case 3:\ (a)[x] = 0;x++;\ case 2:\ _mm_storeu_ps((float *)((a)+x),_mm_setzero_ps());break;\ case 1:\ (a)[x] = 0;\ break;\ case 0:\ break;\ };\ }\ else if(8 == sizeof(*(a))){\ switch(nx){\ case 7:\ (a)[x] = 0;x++;\ case 6:\ (a)[x] = 0;x++;\ case 5:\ (a)[x] = 0;x++;\ case 4:\ _mm_storeu_ps((float *)((a)+x),_mm_setzero_ps());break;\ case 3:\ (a)[x] = 0;x++;\ case 2:\ ((long long *)(a))[x] = 0;break;\ case 1:\ (a)[x] = 0;\ break;\ case 0:\ break;\ };\ }\ } 

我不会说这是最快的方法,因为我不是一个低级别的优化专家。 相反,它是一个正确的架构依赖实现比memset更快的一个例子。

现在,结果。 我计算了静态和dynamic分配的大小为100的int和long long数组的性能,但是除了在静态数组上执行死代码消除的msvc之外,结果是非常可比的,所以我只显示dynamic数组的性能。 时间标记是100万次迭代的ms,使用time.h的低精度时钟函数。

叮当3.8(使用clang-cl前端,优化标志= / OX / arch:AVX / Oi / Ot)

 int: memset: 99 fill: 97 ZERO: 98 intrin_ZERO: 90 long long: memset: 285 fill: 286 ZERO: 285 intrin_ZERO: 188 

gcc 5.1.0(优化标志:-O3 -march = native -mtune = native -mavx):

 int: memset: 268 fill: 268 ZERO: 268 intrin_ZERO: 91 long long: memset: 402 fill: 399 ZERO: 400 intrin_ZERO: 185 

msvc 2015(优化标志:/ OX / arch:AVX / Oi / Ot):

 int memset: 196 fill: 613 ZERO: 221 intrin_ZERO: 95 long long: memset: 273 fill: 559 ZERO: 376 intrin_ZERO: 188 

有很多有趣的事情发生在这里:llvm杀死gcc,MSVC的典型的斑点优化(它在静态数组上执行了一个令人印象深刻的死代码消除,然后有可怕的填充性能)。 虽然我的实现速度要快得多,但这可能只是因为它认识到比特清除的开销要比任何其他设置操作less得多。

铿锵的实施值得更多的关注,因为它显着更快。 一些额外的testing表明它的memset实际上专门用于零 – 对于400字节的数组,非零的memset非常慢(〜220ms),并且与gcc相当。 然而,使用800字节数组的非零memsetting并没有速度差别,这可能是为什么在这种情况下,他们的memset性能比我的实现更差 – 专门化只适用于小数组,而cuttoff恰好在800字节左右。 还要注意,gcc'fill'和'ZERO'没有优化memset(查看生成的代码),gcc只是生成具有相同性能特征的代码。

结论:memset并没有真正优化这个任务,人们也会假装它(否则gcc和msvc和llvm的memset会有相同的性能)。 如果性能很重要,那么memset不应该是最终的解决scheme,特别是对于这些笨拙的中等规模的数组,因为它不是专门用于位清除的,并且不是手工优化的,比编译器本身可以做得更好。

您可以使用memset ,但仅仅是因为我们select的types仅限于整型。

一般情况下,在C中实现一个macros是有意义的

 #define ZERO_ANY(T, a, n) do{\ T *a_ = (a);\ size_t n_ = (n);\ for (; n_ > 0; --n_, ++a_)\ *a_ = (T) { 0 };\ } while (0) 

这会给你类似于C ++的function,让你可以将任何types的对象数组“重置为零”,而不需要像memset这样的黑客行为。 基本上,这是C ++函数模板的C语言模拟,不同之处在于您必须明确指定types参数。

最重要的是,你可以为非腐蚀的数组构build一个“模板”

 #define ARRAY_SIZE(a) (sizeof (a) / sizeof *(a)) #define ZERO_ANY_A(T, a) ZERO_ANY(T, (a), ARRAY_SIZE(a)) 

在你的例子中,它将被应用为

 int a[100]; ZERO_ANY(int, a, 100); // or ZERO_ANY_A(int, a); 

值得注意的是,对于标量types的对象,可以实现一个types无关的macros

 #define ZERO(a, n) do{\ size_t i_ = 0, n_ = (n);\ for (; i_ < n_; ++i_)\ (a)[i_] = 0;\ } while (0) 

 #define ZERO_A(a) ZERO((a), ARRAY_SIZE(a)) 

把上面的例子变成

  int a[100]; ZERO(a, 100); // or ZERO_A(a); 

对于静态声明,我认为你可以使用:

 T myarray[100] = {0}; 

对于dynamic声明,我build议以同样的方式: memset

zero(myarray); 是你所需要的所有C ++。

只需将其添加到另一个文件中:

 template<typename T, size_t SIZE> inline void zero(T(&arr)[SIZE]){ memset(arr, 0, SIZE*sizeof(T)); } 

这是我使用的function:

 template<typename T> static void setValue(T arr[], size_t length, const T& val) { std::fill(arr, arr + length, val); } template<typename T, size_t N> static void setValue(T (&arr)[N], const T& val) { std::fill(arr, arr + N, val); } 

你可以这样调用它:

 //fixed arrays int a[10]; setValue(a, 0); //dynamic arrays int *d = new int[length]; setValue(d, length, 0); 

以上是比使用memset更多的C ++ 11方法。 如果使用指定大小的dynamic数组,则会得到编译时错误。