Mat*_*lia 155
memset(from <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
Run Code Online (Sandbox Code Playgroud)
顺便说一下,在C++中,惯用的方法是使用std::fill(from <algorithm>):
std::fill(myarray, myarray+N, 0);
Run Code Online (Sandbox Code Playgroud)
这可以被自动优化成memset; 我敢肯定,这将工作以最快的速度memset为intS,虽然它可能对较小的类型,如果优化器是不够聪明执行略差.不过,如果有疑问,请介绍一下.
Ben*_*min 16
这个问题,虽然相当陈旧,但需要一些基准,因为它要求的不是最惯用的方式,或者可以用最少的行数写出的方式,而是最快的方式.没有一些实际的测试,回答这个问题是愚蠢的.所以我比较了四个解决方案,memset与std :: fill对比AnT的答案与使用AVX内在函数的解决方案的ZERO.
请注意,此解决方案不是通用的,它仅适用于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(n-x){\
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(n-x){\
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;\
};\
}\
}
Run Code Online (Sandbox Code Playgroud)
我不会声称这是最快的方法,因为我不是一个低级别的优化专家.相反,它是一个正确的架构相关实现的例子,比memset更快.
现在,进入结果.我计算了100个int和long long数组的性能,无论是静态还是动态分配,但除了msvc之外,它在静态数组上消除了死代码,结果非常可比,所以我只展示动态数组性能.使用time.h的低精度时钟功能,时间标记为100万次迭代.
clang 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
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
这里有很多有趣的内容:llvm杀死gcc,MSVC的典型参差优化(它在静态数组上消除了令人印象深刻的死代码,然后填充性能很差).虽然我的实现速度明显更快,但这可能只是因为它认识到位清除的开销比任何其他设置操作都少得多.
Clang的实现值得关注,因为它的速度要快得多.一些额外的测试表明,它的memset实际上专门用于零 - 非零存储器,400字节阵列要慢得多(~220ms)并且与gcc相当.但是,使用800字节数组的非零memset不会产生速度差异,这可能就是为什么在这种情况下,它们的memset性能比我的实现更差 - 专门化仅适用于小型数组,而截止时间大约为800字节.另请注意,gcc'fill'和'ZERO'没有优化到memset(查看生成的代码),gcc只是生成具有相同性能特征的代码.
结论:memset并没有真正优化这个任务,人们会假装它(否则gcc和msvc和llvm的memset将具有相同的性能).如果性能很重要,那么memset不应该是最终解决方案,特别是对于这些笨拙的中型阵列,因为它不专门用于位清除,并且它没有比编译器自己做的更优化.
Ale*_*lds 11
来自memset():
memset(myarray, 0, sizeof(myarray));
Run Code Online (Sandbox Code Playgroud)
sizeof(myarray)如果myarray在编译时已知大小,则可以使用.否则,如果您使用动态大小的数组,例如通过malloc或获得new,则需要跟踪长度.
您可以使用memset,但仅仅因为我们选择的类型仅限于整数类型.
在C的一般情况下,实现宏是有意义的
#define ZERO_ANY(T, a, n) do{\
T *a_ = (a);\
size_t n_ = (n);\
for (; n_ > 0; --n_, ++a_)\
*a_ = (T) { 0 };\
} while (0)
Run Code Online (Sandbox Code Playgroud)
这将为您提供类似C++的功能,可以让您"重置为0"任意类型的对象数组,而不必诉诸于黑客memset.基本上,这是C++函数模板的C模拟,除了你必须明确指定类型参数.
最重要的是,您可以为非衰减数组构建"模板"
#define ARRAY_SIZE(a) (sizeof (a) / sizeof *(a))
#define ZERO_ANY_A(T, a) ZERO_ANY(T, (a), ARRAY_SIZE(a))
Run Code Online (Sandbox Code Playgroud)
在您的示例中,它将应用为
int a[100];
ZERO_ANY(int, a, 100);
// or
ZERO_ANY_A(int, a);
Run Code Online (Sandbox Code Playgroud)
值得注意的是,特别是对于标量类型的对象,可以实现与类型无关的宏
#define ZERO(a, n) do{\
size_t i_ = 0, n_ = (n);\
for (; i_ < n_; ++i_)\
(a)[i_] = 0;\
} while (0)
Run Code Online (Sandbox Code Playgroud)
和
#define ZERO_A(a) ZERO((a), ARRAY_SIZE(a))
Run Code Online (Sandbox Code Playgroud)
把上面的例子变成了
int a[100];
ZERO(a, 100);
// or
ZERO_A(a);
Run Code Online (Sandbox Code Playgroud)