【发布时间】:2021-10-24 03:48:39
【问题描述】:
我一直在玩 SIMD OMP 指令,但我没有让编译器在我的场景中发出 ANDPS。
我正在尝试做的事情:
- 这是problem 的一个实现(tldr:查找具有共同朋友的用户对)。我的方法是将 64 位(无论某人是不是朋友)打包到
unsigned long long中。 - 我的 SIMD 方法:将
AND置于两个关系向量之间,将reduce与OR完美匹配OMP 的reduction pattern。
g++ 指令(在 2019 intel i-7 macbookPro 上):
g++-11 friends.cpp -S -O3 -fopenmp -fsanitize=address -Wshadow -Wall -march=native --std=c++17;
下面是我的实现
#include <vector>
#include <algorithm>
#include "iostream"
#include <cmath>
#include <numeric>
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
ull find_sol(vector<vector<ull>> & input_data, int q) {
bool not_friend = false;
ull cnt = 0;
int size_arr = (int) input_data[0].size();
for (int i = 0; i < q; ++i) // from these friends
{
for (int j = i+1; j < q; ++j) // to these friends
{
int step = j/64;
int remainder = j - 64*step;
not_friend = (input_data[i].at(step) >> remainder) % 2 == 0;
if(not_friend){
bool counter = false;
vector<ull> & v1 = input_data[i];
vector<ull> & v2 = input_data[j];
#pragma omp simd reduction(|:counter)
for (int c = 0; c < size_arr; ++c)
{
__asm__ ("entry");
counter |= (v1[c] & v2[c])>0;
__asm__ ("exit");
}
if(counter>0)
cnt++;
}
}
}
return cnt << 1;
}
int main(){
int q;
cin >> q;
vector<vector<ull>> input_data(q,vector<ull>(1 + q/64,0ULL));
for (int i = 0; i < q; ++i)
{
string s;
cin >> s;
for (int j = 0; j < 1 + q/64; ++j)
{
string str = s.substr(j*64,64);
reverse(str.begin(),str.end());
ull ul = std::stoull(str,nullptr,2);
input_data.at(i).at(j) = ul;
}
}
cout << find_sol(input_data,q) << endl;
}
查看循环内的程序集,我希望有一些 SIMD 指令(特别是 andps),但我看不到它们。是什么阻止我的编译器发出它们?另外,编译器有没有办法发出警告 re:what's wrong(会很有帮助)?
entry
# 0 "" 2
cmpb $0, (%rbx)
jne L53
movq (%r8), %rdx
leaq 0(,%rax,8), %rdi
addq %rdi, %rdx
movq %rdx, %r15
shrq $3, %r15
cmpb $0, (%r15,%rcx)
jne L54
cmpb $0, (%r11)
movq (%rdx), %rdx
jne L55
addq (%r9), %rdi
movq %rdi, %r15
shrq $3, %r15
cmpb $0, (%r15,%rcx)
jne L56
andq (%rdi), %rdx
movzbl (%r12), %edx
setne %dil
cmpb %r13b, %dl
jg L21
testb %dl, %dl
jne L57
L21:
orb %dil, -32(%r10)
编辑 1:
按照 Peter 的第 1 条和第 2 条建议,我将标记移出循环,并用简单的 OR 替换了二值化。我仍然没有收到 SIMD 指令:
ull counter = 0;
vector<ull> & v1 = input_data[i];
vector<ull> & v2 = input_data[j];
__asm__ ("entry" :::);
#pragma omp simd reduction(|:counter)
for (int c = 0; c < size_arr; ++c)
{
counter |= v1[c] & v2[c];
}
__asm__ ("exit" :::);
if(counter!=0)
cnt++;
【问题讨论】:
-
在最近的 GCC 中,像
__asm__ ("entry");这样的非空基本 Asm 语句有一个隐含的::: "memory"破坏,使得编译器无法跨迭代组合数组访问。如果你真的想要这些标记,也许可以试试__asm__ ("entry" :::);。 (没有内存破坏器的扩展 asm)。 -
另外,你为什么用
(v1[c] & v2[c])>0;进行布尔运算?这将需要 SSE4.1pcmpeqq自动矢量化,而不是仅 OR 然后检查counter的!=0可以水平 OR 并最终只检查单个标量 uint64_t 是否为非零。 -
谢谢彼得。我根据您的建议更新了代码,但仍然没有收到 SIMD 指令。如果您有其他建议,请告诉我!谢谢
-
-fsanitize=address为什么?这使得编译器更难优化…… -
@MarcGlisse:确实,没有它和没有
asm语句,它可以矢量化。 (OpenMP 向量化很疯狂,使用vpmuldq(索引相乘?)和vpgatherqq和GCC11.2,但普通的-ftree-vectorize向量化是正常的。)
标签: c++ performance bit-manipulation openmp simd