Axa*_*dax 31 c arrays performance
假设我们有一系列这样的整数:
const int size = 100000;
int array[size];
//set some items to 0 and other items to 1
Run Code Online (Sandbox Code Playgroud)
我想将所有值为1的项替换为另一个值,例如123456.这可以通过以下方式轻松实现:
for(int i = 0; i < size ; i++){
if(array[i] != 0)
array[i] = 123456;
}
Run Code Online (Sandbox Code Playgroud)
出于好奇,有没有更快的方法来做到这一点,通过某种x86技巧,或者这是处理器的最佳代码?
Nic*_*rca 47
对于最初具有0和1的特定情况,以下可能更快.你必须对它进行基准测试.尽管如此,你可能无法用普通的C做得更好; 如果你想利用可能存在的"x86技巧",你可能需要深入装配.
for(int i = 0; i < size ; i++){
array[i] *= 123456;
}
Run Code Online (Sandbox Code Playgroud)
基准代码:
#include <time.h>
#include <stdlib.h>
#include <stdio.h>
size_t diff(struct timespec *start, struct timespec *end)
{
return (end->tv_sec - start->tv_sec)*1000000000 + end->tv_nsec - start->tv_nsec;
}
int main(void)
{
const size_t size = 1000000;
int array[size];
for(size_t i=0; i<size; ++i) {
array[i] = rand() & 1;
}
struct timespec start, stop;
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &start);
for(size_t i=0; i<size; ++i) {
array[i] *= 123456;
//if(array[i]) array[i] = 123456;
}
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &stop);
printf("size: %zu\t nsec: %09zu\n", size, diff(&start, &stop));
}
Run Code Online (Sandbox Code Playgroud)
计算机:四核AMD Phenom @ 2.5GHz,Linux,GCC 4.7,编译
$ gcc arr.c -std=gnu99 -lrt -O3 -march=native
Run Code Online (Sandbox Code Playgroud)
if 版本:~5-10ms*= 版本:~1.3msSom*_*ude 15
对于像你这样的小数组试图找到另一种算法是没有用的,如果这些值不是特定的模式,那么简单的循环是唯一的方法.
但是,如果你有一个非常大的数组(我们正在谈论几百万个条目),那么你可以将工作分成线程.每个单独的线程处理整个数据集的较小部分.
gwi*_*rrr 13
您可能也希望对此进行基准测试:
for(int i = 0; i < size ; i++){
array[i] = (~(array[i]-1) & 123456);
}
Run Code Online (Sandbox Code Playgroud)
我通过与SchighSchagh相同的基准测试来运行它,在我的设置上几乎没有差别.但是,它可能会有所不同.
编辑:停止按下!
我记得如果":"之间的参数是常量,x86可以"取消分支"三元运算符.考虑以下代码:
for(size_t i=0; i<size; ++i) {
array[i] = array[i] ? 123456 : 0;
}
Run Code Online (Sandbox Code Playgroud)
看起来几乎像你的原始代码,不是吗?好吧,反汇编显示它已经编译而没有任何分支:
for(size_t i=0; i<size; ++i) {
00E3104C xor eax,eax
00E3104E mov edi,edi
array[i] = array[i] ? 123456 : 0;
00E31050 mov edx,dword ptr [esi+eax*4]
00E31053 neg edx
00E31055 sbb edx,edx
00E31057 and edx,1E240h
00E3105D mov dword ptr [esi+eax*4],edx
00E31060 inc eax
00E31061 cmp eax,5F5E100h
00E31066 jb wmain+50h (0E31050h)
}
Run Code Online (Sandbox Code Playgroud)
在性能方面,它似乎与我原来的和SchighSchagh解决方案相提并论.但它更具可读性和灵活性.例如,它可以用于具有不同于0和1的值的array [i].
底线,基准和偷看拆解.
该阵列足够小,适合缓存,因此使用SIMD值得:(未经测试)
mov ecx, size
lea esi, [array + ecx * 4]
neg ecx
pxor xmm0, xmm0
movdqa xmm1, [_vec4_123456] ; value of { 123456, 123456, 123456, 123456 }
_replaceloop:
movdqa xmm2, [esi + ecx * 4] ; assumes the array is 16 aligned, make that true
add ecx, 4
pcmpeqd xmm2, xmm0
pandn xmm2, xmm1
movdqa [esi + ecx * 4 - 16], xmm2
jnz _replaceloop
Run Code Online (Sandbox Code Playgroud)
展开2可能会有所帮助.
如果你有SSE4.1,你可以使用SchighSchagh的乘法技巧pmulld.