use*_*367 9 c memory optimization performance
没有裂变的代码看起来像这样:
int check(int * res, char * map, int n, int * keys){
int ret = 0;
for(int i = 0; i < n; ++i){
res[ret] = i;
ret += map[hash(keys[i])]
}
return ret;
}
Run Code Online (Sandbox Code Playgroud)
裂变:
int check(int * res, char * map, int n, int * keys){
int ret = 0;
for(int i = 0; i < n; ++i){
tmp[i] = map[hash(keys[i])];
}
for(int i = 0; i < n; ++i){
res[ret] = i;
ret += tmp[i];
}
return ret;
}
Run Code Online (Sandbox Code Playgroud)
笔记:
瓶颈是map[hash(keys[i])]随机访问内存.
通常,这是if(tmp[i]) res[ret++] = i;为了避免if,我正在使用ret += tmp[i].
map[..] 总是0或1
裂变版本通常明显更快,我试图解释原因.我最好的猜测是ret += map[..]仍然会引入一些依赖性并阻止推测性执行.
我想听听是否有人有更好的解释.
根据我的测试,我在融合循环和分离循环之间获得大约2倍的速度差异.无论我如何调整循环,这种速度差异都非常一致.
Fused: 1.096258 seconds
Split: 0.562272 seconds
Run Code Online (Sandbox Code Playgroud)
(有关完整的测试代码,请参阅底部.)
虽然我不是百分百肯定,但我怀疑这是由于两件事的结合:
map[gethash(keys[i])].很明显,map[gethash(keys[i])]几乎每次都会导致缓存未命中.实际上,它可能足以使整个加载存储缓冲区饱和.
现在让我们看一下添加的依赖项.问题是ret变量:
int check_fused(int * res, char * map, int n, int * keys){
int ret = 0;
for(int i = 0; i < n; ++i){
res[ret] = i;
ret += map[gethash(keys[i])];
}
return ret;
}
Run Code Online (Sandbox Code Playgroud)
该商店的地址解析需要该ret变量.res[ret] = i;
ret来自确定的缓存未命中.ret即将到来tmp[i]- 这要快得多.融合循环情况的地址解析的这种延迟可能导致res[ret] = i存储阻塞加载存储缓冲区map[gethash(keys[i])].
由于加载存储缓冲区具有固定的大小,但是其中有两倍的垃圾:
您只能将缓存未命中的重叠量减少一半.因此减速2倍.
假设我们将融合循环更改为:
int check_fused(int * res, char * map, int n, int * keys){
int ret = 0;
for(int i = 0; i < n; ++i){
res[i] = i; // Change "res" to "i"
ret += map[gethash(keys[i])];
}
return ret;
}
Run Code Online (Sandbox Code Playgroud)
这将破坏地址解析依赖性.
(请注意,它不再相同,但它只是为了演示性能差异.)
然后我们得到类似的时间:
Fused: 0.487477 seconds
Split: 0.574585 seconds
Run Code Online (Sandbox Code Playgroud)
这是完整的测试代码:
#define SIZE 67108864
unsigned gethash(int key){
return key & (SIZE - 1);
}
int check_fused(int * res, char * map, int n, int * keys){
int ret = 0;
for(int i = 0; i < n; ++i){
res[ret] = i;
ret += map[gethash(keys[i])];
}
return ret;
}
int check_split(int * res, char * map, int n, int * keys, int *tmp){
int ret = 0;
for(int i = 0; i < n; ++i){
tmp[i] = map[gethash(keys[i])];
}
for(int i = 0; i < n; ++i){
res[ret] = i;
ret += tmp[i];
}
return ret;
}
int main()
{
char *map = (char*)calloc(SIZE,sizeof(char));
int *keys = (int*)calloc(SIZE,sizeof(int));
int *res = (int*)calloc(SIZE,sizeof(int));
int *tmp = (int*)calloc(SIZE,sizeof(int));
if (map == NULL || keys == NULL || res == NULL || tmp == NULL){
printf("Memory allocation failed.\n");
system("pause");
return 1;
}
// Generate Random Data
for (int i = 0; i < SIZE; i++){
keys[i] = (rand() & 0xff) | ((rand() & 0xff) << 16);
}
printf("Start...\n");
double start = omp_get_wtime();
int ret;
ret = check_fused(res,map,SIZE,keys);
// ret = check_split(res,map,SIZE,keys,tmp);
double end = omp_get_wtime();
printf("ret = %d",ret);
printf("\n\nseconds = %f\n",end - start);
system("pause");
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
989 次 |
| 最近记录: |