以下代码生成的程序集在使用-O3
. 为了完整起见,代码始终在 GCC 13.2 中执行 SIMD,而从不在 clang 17.0.1 中执行 SIMD。
#include <array>
__attribute__((noinline)) void fn(std::array<int, 4>& lhs, const std::array<int, 4>& rhs)
{
for (std::size_t idx = 0; idx != 4; ++idx) {
lhs[idx] = lhs[idx] + rhs[idx];
}
}
Run Code Online (Sandbox Code Playgroud)
这是Godbolt 中的链接。
这是 GCC 12.3 的实际汇编(使用 -O3):
fn(std::array<int, 4ul>&, std::array<int, 4ul> const&):
lea rdx, [rsi+4]
mov rax, rdi
sub rax, rdx
cmp rax, 8
jbe .L2
movdqu xmm0, XMMWORD PTR [rsi]
movdqu xmm1, XMMWORD PTR [rdi]
paddd …
Run Code Online (Sandbox Code Playgroud) 考虑修复的这个最小实现vector<int>
:
constexpr std::size_t capacity = 1000;
struct vec
{
int values[capacity];
std::size_t _size = 0;
std::size_t size() const noexcept
{
return _size;
}
void push(int x)
{
values[size()] = x;
++_size;
}
};
Run Code Online (Sandbox Code Playgroud)
鉴于以下测试用例:
vec v;
for(std::size_t i{0}; i != capacity; ++i)
{
v.push(i);
}
asm volatile("" : : "g"(&v) : "memory");
Run Code Online (Sandbox Code Playgroud)
编译器生成非向量化程序集:godbolt.org上的实例
如果我做出以下任何改变......
values[size()]
- > values[_size]
添加__attribute__((always_inline))
到size()
...然后编译器生成矢量化程序集:godbolt.org上的实例
这是一个gcc bug吗?或者有一个原因可以解释为什么一个简单的访问器size()
会阻止自动矢量化,除非always_inline
明确添加?
考虑以下valarray
类:
#include <stdlib.h>
struct va
{
void add1(const va& other);
void add2(const va& other);
size_t* data;
size_t size;
};
void va::add1(const va& other) {
for (size_t i = 0; i < size; ++i) {
data[i] += other.data[i];
}
}
void va::add2(const va& other){
for (size_t i = 0, s = size; i < s; ++i) {
data[i] += other.data[i];
}
}
Run Code Online (Sandbox Code Playgroud)
该add2
函数针对不同的编译器(MSVC、Clang、GCC、ICC)进行了向量化,而add1
并非如此。参见https://godbolt.org/z/c61qvrrbv
这是通过潜在的别名来解释的:编译器无法证明所指向的元素之一data
不是其size
本身。
data
然而,和指向的元素也可能存在重叠other.data
。对于 MSVC,这些元素和指针本身可能存在别名,因为它没有利用严格别名规则。这适用于add1 …
c++ vectorization strict-aliasing compiler-optimization auto-vectorization
以下是执行相同操作的自由函数,但在第一种情况下,循环不是矢量化的,但在其他情况下它是.这是为什么?
#include <vector>
typedef std::vector<double> Vec;
void update(Vec& a, const Vec& b, double gamma) {
const size_t K = a.size();
for (size_t i = 0; i < K; ++i) { // not vectorized
a[i] = b[i] * gamma - a[i];
}
}
void update2(Vec& a, const Vec& b, double gamma) {
for (size_t i = 0; i < a.size(); ++i) { // vectorized
a[i] = b[i] * gamma - a[i];
}
}
void update3(Vec& a, size_t K, const Vec& b, …
Run Code Online (Sandbox Code Playgroud) 新的RyuJIT编译器是否曾生成向量(SIMD)CPU指令,何时?
附注:System.Numerics命名空间包含允许显式使用Vector操作的类型,这些操作可能会也可能不会生成SIMD指令,具体取决于CPU,CLR版本,JITer版本,是否直接编译为本机代码.这个问题具体是关于何时非矢量代码(例如在C#或F#中)将产生SIMD指令.
我尝试对 16 位整数 ARGB 通道的 64 位颜色的预乘进行矢量化。
我很快意识到,由于缺乏加速整数除法支持,我需要将我的值转换为float
并显式使用一些 SSE2/SSE4.1 内在函数以获得最佳性能。尽管如此,我还是想保留非特定的通用版本作为后备解决方案(我知道它目前比某些普通操作慢,但它将提供未来可能的改进的兼容性)。
但是,在我的机器上结果不正确。
一个非常小的重现:
// Test color with 50% alpha
(ushort A, ushort R, ushort G, ushort B) c = (0x8000, 0xFFFF, 0xFFFF, 0xFFFF);
// Minimal version of the fallback logic if HW intrinsics cannot be used:
Vector128<uint> v = Vector128.Create(c.R, c.G, c.B, 0u);
v = v * c.A / Vector128.Create(0xFFFFu);
var cPre = (c.A, (ushort)v[0], (ushort)v[1], (ushort)v[2]);
// Original color:
Console.WriteLine(c); // prints (32768, 65535, 65535, 65535)
// Expected …
Run Code Online (Sandbox Code Playgroud) 请考虑以下最小示例:
#cython: language_level=3, boundscheck=False, wraparound=False, initializedcheck=False, cdivision=True
cimport cython
from libc.stdlib cimport malloc
def main(size_t ni, size_t nt, size_t nx):
cdef:
size_t i, j, t, x, y
double[:, :, ::1] a = <double[:ni, :ni, :nx]>malloc(ni * ni * nx * sizeof(double))
double[:, :, ::1] b = <double[:nt, :ni, :nx]>malloc(nt * ni * nx * sizeof(double))
size_t[:, :, ::1] best = <size_t[:nt, :ni, :nx]>malloc(nt * ni * nx * sizeof(size_t))
size_t mxi
double s, mxs
for t in range(nt):
for j …
Run Code Online (Sandbox Code Playgroud) 我已经为卷积矩阵实现了以下程序
#include <stdio.h>
#include <time.h>
#define NUM_LOOP 1000
#define N 128 //input or output dimention 1
#define M N //input or output dimention 2
#define P 5 //convolution matrix dimention 1 if you want a 3x3 convolution matrix it must be 3
#define Q P //convolution matrix dimention 2
#define Csize P*Q
#define Cdiv 1 //div for filter
#define Coffset 0 //offset
//functions
void unusual(); //unusual implementation of convolution
void naive();
//data
unsigned short int input[N][M] __attribute__(( aligned(32))); // input …
Run Code Online (Sandbox Code Playgroud) Arstechnia最近有一篇文章为什么一些编程语言比其他语言更快.它比较了Fortran和C,并提到了求和数组.在Fortran中,假设数组不重叠,从而允许进一步优化.在C/C++中,指向相同类型的指针可能会重叠,因此通常不能使用此优化.但是,在C/C++中,可以使用restrict
or __restrict
关键字告诉编译器不要假设指针重叠.所以我开始研究自动矢量化.
以下代码在GCC和MSVC中进行矢量化
void dot_int(int *a, int *b, int *c, int n) {
for(int i=0; i<n; i++) {
c[i] = a[i] + b[i];
}
}
Run Code Online (Sandbox Code Playgroud)
我使用和不使用重叠数组测试了它,它得到了正确的结果.但是,我使用SSE手动向量化循环的方式不能处理重叠数组.
int i=0;
for(; i<n-3; i+=4) {
__m128i a4 = _mm_loadu_si128((__m128i*)&a[i]);
__m128i b4 = _mm_loadu_si128((__m128i*)&b[i]);
__m128i c4 = _mm_add_epi32(a4,b4);
_mm_storeu_si128((__m128i*)c, c4);
}
for(; i<n; i++) {
c[i] = a[i] + b[i];
}
Run Code Online (Sandbox Code Playgroud)
接下来我尝试使用__restrict
.我假设由于编译器可以假设数组不重叠,它不会处理重叠数组,但GCC和MSVC仍然可以获得重叠数组的正确结果,即使是__restrict
.
void dot_int_restrict(int * __restrict a, int * __restrict b, int * __restrict c, …
Run Code Online (Sandbox Code Playgroud) 我知道“为什么我的编译器要这样做”不是最好的问题类型,但这个问题对我来说真的很奇怪,我很困惑。
我原以为这std::min()
与手写的三元相同(可能还有一些编译时模板的东西),并且在正常使用时似乎可以编译为相同的操作。但是,当尝试使“最小和总和”循环自动向量化时,它们似乎不一样,如果有人能帮我找出原因,我会很高兴。这是一个产生问题的小示例代码:
#pragma GCC target ("avx2")
#pragma GCC optimize ("O3")
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#define N (1<<20)
char a[N], b[N];
int main() {
for (int i=0; i<N; ++i) {
a[i] = rand()%100;
b[i] = rand()%100;
}
int ans = 0;
#pragma GCC ivdep
for (int i=0; i<N; ++i) {
//ans += std::min(a[i], b[i]);
ans += a[i]>b[i] ? a[i] : b[i];
}
printf("%d\n", ans);
}
Run Code Online (Sandbox Code Playgroud)
我gcc 9.3.0
用编译命令编译它g++ -o test test.cpp -ftree-vectorize -fopt-info-vec-missed -fopt-info-vec-optimized -funsafe-math-optimizations …
c++ optimization gcc compiler-optimization auto-vectorization