我想对大的移位数组进行异或运算,下面是该函数的可移植版本,以便于解释。我如何改善这种计算?我已经尝试使用AVX 2,但没有看到太大的改善。目前,在下面的例子中显示的数据库需要50毫秒来处理所有内容,这是12 GB/s,我将感谢任何改善计算的提示。
#include <iostream>
uint64_t partition_size = 4096;
uint64_t entry_size = 32; // bytes
uint64_t DB_size = 16777216;
uint64_t *DB = new uint64_t[DB_size * entry_size/64];
//partition_index will be a random multiple of partition_size, e.g. 0, 8192, 4096 etc
//random_offset will be a random number in [0, partition_size]
void xor_shifted_arrays(uint32_t partition_index, uint32_t random_offset, uint64_t *result)
{
auto uint64_per_entry = entry_size / sizeof(uint64_t);
int shift_offset;
uint32_t shift;
for (int i = 0; i < partition_size ; i = i + 1)
{
shift = (i + random_offset) & (partition_size - 1);
shift_offset = shift * uint64_per_entry;
for (int j = 0; j < uint64_per_entry; j=j+1){
result[shift_offset + j] = result[shift_offset + j] ^ DB[partition_index + j];
}
partition_index = partition_index + uint64_per_entry;
}
}
字符串
更新:这里是godbolt:https://godbolt.org/z/j14av3fGq也在两个示例上运行了这段代码。
英特尔(R)酷睿(TM)i7- 7700 K CPU@4.20 GHz,运行MacOS 13.6,16 GB DDR4 RAM,编译器Apple clang版本15.0.0(clang-1500.0.40.1)
AWS r7i.2xlarge Intel(R)Xeon(R)Platinum 8488C with Ubuntu,64 GB DDR5 RAM,compiler g++(Ubuntu 11.4.0-1ubuntu1~22.04)11.4.0
令人惊讶的是,它在Xeon上慢了2倍!!
我正在使用编译器标志O3
Update 2:我觉得这可能是有用的,上面的代码从外部函数调用类似这样的东西(不是运行代码)
void outer_function(){
uint64_t *result1 = new uint64_t[partition_size];
uint64_t *result2 = new uint64_t[partition_size];
uint64_t number_partitions = 4096;
for (int i=0; i< number_partitions; i++){
xor_shifted_arrays(i*partition_size, some_rnd_gen(), result1)
}
for (int i=0; i< number_partitions; i++){
xor_shifted_arrays(i*partition_size, some_rnd_gen(), result2)
}
}
型
2条答案
按热度按时间hxzsmxv21#
这些大多是通用的提示,这将帮助您的编译器优化代码(其中许多已经在注解中):
size_t
(或者如果您希望允许负索引,则使用ptrdiff_t
)。这将保存编译器,使其不必处理潜在的回绕或溢出行为。const
。如果编译器知道它们的值,它可以优化与它们的乘法或除法运算,特别是如果它们是2的幂。__restrict
关键字(只有在您确定此数组不会与其他数组别名/重叠时才这样做)。这通常有助于编译器展开和SIMD优化。-march=native
(或-march=target_architecture
)编译代码,以优化本地或某些特定的CPU架构(当然,除了-O3
)。A = A + B
,写A += B
,(与^
/^=
等相同)。这通常对编译器无关紧要。假设你的godbolt-link中的
j=j+4
是一个错别字,你的意思是j+=1
(或++j
),这会导致非常干净的AVX 2代码:字符串
Godbolt-Link:https://godbolt.org/z/Khs5aTPaK
我看不出这段代码有什么进一步的改进。你需要对每个
result
条目和相应的DB
条目进行一次读写。如果你能控制内存管理,你可以确保指针与页面大小对齐(或者至少与你的SIMD宽度对齐)。另外,如果(对于不同的问题)某些条目将被多次读取或修改,您可以尝试重新安排循环,以便减少变量从内存读取或写入内存的频率。
csga3l582#
OpenCL可以轻松地对简单代码进行向量化,并将工作分配给所有内核。(12核),在双通道4800 MHz DDR5内存上提供约44 GB/s带宽(这只是一个简单的所有元素的XOR,不完全是你的算法,所以它没有有效地使用缓存,而是简单地流数据):
字符串
产出:
型
我不得不安装英特尔的GPU才能使用CPU。我也试过GPU,但PCIE v4.0有一些瓶颈(只有32 GB/s左右,在CPU缓存之外)。
要控制L2和L3上的缓存或显式选择核心,您可以使用OpenCL的设备裂变功能。如果您不需要OpenCL,您仍然可以观察程序二进制的汇编输出,以了解它如何将代码向量化,并将其用于您自己的C++手工调优的AVX库。