考虑以下玩具示例,其中A是以n x 2列主要顺序存储的矩阵,我想计算其列总和.sum_0仅计算第1列的总和,同时sum_1也计算第2列的总和.这实际上是一个人为的例子,因为基本上不需要为这个任务定义两个函数(我可以编写一个带有双循环嵌套的函数,其中外循环从中迭代0到j).它的构建是为了演示我现实中的模板问题.
/* "test.c" */
#include <stdlib.h>
// j can be 0 or 1
static inline void sum_template (size_t j, size_t n, double *A, double *c) {
if (n == 0) return;
size_t i;
double *a = A, *b = A + n;
double c0 = 0.0, c1 = 0.0;
#pragma omp simd reduction (+: c0, c1) aligned (a, b: 32)
for (i = 0; i < …Run Code Online (Sandbox Code Playgroud) 在下面的代码中,为什么第二个循环能够自动矢量化但第一个不能?如何修改代码以便自动进行矢量化?gcc说:
注意:没有矢量化:控制循环中的流程.
我使用的是gcc 8.2,标志是-O3 -fopt-info-vec-all.我正在编译x86-64 avx2.
#include <stdlib.h>
#include <math.h>
void foo(const float * x, const float * y, const int * v, float * vec, float * novec, size_t size) {
size_t i;
float bar;
for (i=0 ; i<size ; ++i){
bar = x[i] - y[i];
novec[i] = v[i] ? bar : NAN;
}
for (i=0 ; i<size ; ++i){
bar = x[i];
vec[i] = v[i] ? bar : NAN;
}
}
Run Code Online (Sandbox Code Playgroud)
更新:这会自动执行:
for (i=0 ; i<size ; ++i){ …Run Code Online (Sandbox Code Playgroud) 使用GCC编译器时,该-ftree-vectorize选项会启用自动矢量化,并且在使用时会自动设置此标志-O3。它可以向量化到什么水平?即,我将获得SSE2,SSE4.2,AVX或AVX2指令吗?我知道mavx,mavx2标志等的存在,但是我想知道没有那些特定标志来强制进行特定类型矢量化的编译器在做什么。
C++ 17为标准库添加了并行扩展(例如std::sort(std::execution::par_unseq, arr, arr + 1000),允许使用多个线程和向量指令进行排序).
我注意到微软的实验性实现提到VC++编译器缺乏对这里做矢量化的支持,这让我感到惊讶 - 我认为现代C++编译器能够推断出循环的可矢量化,但显然VC++编译器/优化器无法生成SIMD代码即使明确告知这样做.看似缺乏自动矢量化支持与2011年关于Quora的问题的答案相矛盾,这表明编译器将在可能的情况下进行矢量化.
也许,编译器只会对非常明显的情况进行矢量化,例如a std::array<int, 4>,而且只不过是这样,因此C++ 17的显式并行化会很有用.
因此我的问题是:当没有明确告知这样做时,当前的编译器会自动向量化我的代码吗?(为了使这个问题更具体,让我们将其缩小到支持SIMD的Intel x86 CPU,以及最新版本的GCC,Clang,MSVC和ICC.)
作为扩展:其他语言的编译器是否可以做更好的自动矢量化(可能是由于语言设计)(因此C++标准委员会认为它对于显式(C++ 17风格)矢量化是必要的)?
parallel-processing simd vectorization auto-vectorization c++17
我有这段代码在AMD64兼容CPU上运行Ubuntu 14.04时会出现段错误:
#include <inttypes.h>
#include <stdlib.h>
#include <sys/mman.h>
int main()
{
uint32_t sum = 0;
uint8_t *buffer = mmap(NULL, 1<<18, PROT_READ,
MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
uint16_t *p = (buffer + 1);
int i;
for (i=0;i<14;++i) {
//printf("%d\n", i);
sum += p[i];
}
return sum;
}
Run Code Online (Sandbox Code Playgroud)
如果使用分配内存,则仅此段错误mmap.如果我使用malloc,堆栈上的缓冲区,或全局变量,它不会段错误.
如果我将循环的迭代次数减少到少于14的次数,则不再是段错误.如果我从循环内打印数组索引,它也不再是段错误.
为什么未对齐的内存访问能够访问未对齐地址的CPU上的段错误,为什么只有在这种特定情况下呢?
我\xe2\x80\x99m 正在学习并行/分布式计算课程,并且想知道 Swift 编译器是否执行任何自动矢量化来优化循环。我知道 LLVM 执行了很多(全部?)优化。我发现这个页面详细介绍了它的一些自动矢量化优化,其中指出它默认处于打开状态:( https://llvm.org/docs/Vectorizers.html#the-loop-vectorizer )
\n\n我想知道 Swift 是否仍然如此,因为它\xe2\x80\x99s 构建在 LLVM 之上。这些优化也会发生在 SIL 或 IR 级别吗?我\xe2\x80\x99m是编译器新手,所以如果我的理解不正确,请纠正我。谢谢
\ncompiler-construction compiler-optimization auto-vectorization swift
如果我接受这个代码
#include <cmath>
void compute_sqrt(const double* x, double* y, int n) {
int i;
#pragma omp simd linear(i)
for (i=0; i<n; ++i) {
y[i] = std::sqrt(x[i]);
}
}
Run Code Online (Sandbox Code Playgroud)
并使用 进行编译g++ -S -c -O3 -fopenmp-simd -march=cascadelake,然后我在循环中得到这样的指令(编译器-资源管理器)
...
vsqrtsd %xmm0, %xmm0, %xmm0
...
Run Code Online (Sandbox Code Playgroud)
XMM 是 128 位寄存器,但 Cascadelake 支持 avx-512。有没有办法让 gcc 使用 256 (YMM) 或 512 位 (ZMM) 寄存器?
相比之下,ICC 默认使用 256 个寄存器进行 Cascadelake:使用icc -c -S -O3 -march=cascadelake -qopenmp-simdProduces 进行编译 ( compiler-explorer )
...
vsqrtpd 32(%rdi,%r9,8), …Run Code Online (Sandbox Code Playgroud) 当前示例显示在三层循环内执行 N=10 个独立操作,但不幸的是,英特尔编译器自动向量化在循环级别计算成本,当在最内层成功时,它拒绝考虑两个“外部”的向量化。 ' 循环。
一种解决方案是仅使用一个循环来表达相同的内容,其中允许但可能不鼓励使用“if-else”条件。
索引涵盖了所有可能组合的一个子集,如果有人知道它的正式名称那就太好了。
下面是 L=5(即 10 种可能的组合)的最小工作示例。生产代码大量使用了 L=6 的情况,其中有 20 个操作被执行约 100k 次(即对于长度为 6 的不同数组)。
std::cout注意:最内层循环的自动矢量化已经过测试,没有在每次迭代中使用打印:-)
注意:出于好奇,预先计算元素的(当前)策略(仅内循环矢量化,英特尔报告显示“估计潜在加速:1.8”)而不是仅在需要时动态计算它们,使该部分占 12%慢点。
#include <iostream>
int vectorizable_function(int &A, int &B, int &C) { return A + B - C; }
void populate_combinations(int *container, int L, int *data){
// Intel vectorization report provides feedback
//
//
int counter=0;
for (int i=0; i<L-2; i++){
for (int j=i+1; j<L-1; j++){
for (int k=j+1; k<L; k++){
// std::cout << "i,j,k are " << i …Run Code Online (Sandbox Code Playgroud) 我有两种点积实现:一种是手工编码的https://godbolt.org/z/48EEnnY4r
int bla2(const std::vector<int>& a, const std::vector<int>& b){
int res = 0;
for(size_t i=0; i < a.size(); ++i){
res += a[i]*b[i];
}
return res;
}
Run Code Online (Sandbox Code Playgroud)
一个使用 C++23 的std::views::zip https://godbolt.org/z/TsGW1WYnf
int bla(const std::vector<int>& a, const std::vector<int>& b){
int res = 0;
for(const auto& [x,y] : std::views::zip(a,b)){
res += x*y;
}
return res;
}
Run Code Online (Sandbox Code Playgroud)
在 godbolt 中,手工编码版本使用了大量 SIMD 指令,而基于 zip 的实现则没有。这里发生了什么?如果我使用迭代器实现它,它也会获得 SIMD。我认为在幕后范围只使用迭代器。这些表达式不等价吗?
我尝试编写一些函数来使用单个矩阵和源向量数组来执行矩阵向量乘法。我曾经用 C++ 编写过这些函数,并在 x86 AVX512 汇编中编写过一次,以将性能与英特尔 VTune Profiler 进行比较。当使用源向量数组作为目标数组时,汇编变体的执行速度比 C++ 对应版本快 3.5 倍到 10x\xc2\xa0,但是当使用不同的源和目标数组时,汇编变体的性能几乎不比 C++ 对应版本更好,实现几乎相同的性能...有时甚至更糟。
\n我无法理解的另一件事是,为什么在使用不同的源和目标数组时,C++ 对应项甚至可以达到与汇编变体接近相同或更好的性能水平,即使汇编代码要短得多并且也根据静态分析工具 uica 和 llvm-mca 速度提高数倍。uica.uops.info
\n我不想让这篇文章变得太长,所以我只发布执行 mat4-vec4 乘法的函数的代码。
\n这是汇编变体的代码,它假设矩阵要转置:
\nalignas(64) uint32_t mat4_mul_vec4_avx512_vpermps_index[64]{ 0, 0, 0, 0, 4, 4, 4, 4, 8, 8, 8, 8, 12, 12, 12, 12,\n 1, 1, 1, 1, 5, 5, 5, 5, 9, 9, 9, 9, 13, 13, 13, 13,\n 2, 2, 2, 2, 6, 6, 6, 6, 10, 10, 10, 10, 14, 14, …Run Code Online (Sandbox Code Playgroud)