Arstechnia最近有一篇文章为什么一些编程语言比其他语言更快.它比较了Fortran和C,并提到了求和数组.在Fortran中,假设数组不重叠,从而允许进一步优化.在C/C++中,指向相同类型的指针可能会重叠,因此通常不能使用此优化.但是,在C/C++中,可以使用restrictor __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) 在过去的几天里,我一直在阅读gcc 4.7的自动向量化.我按照我在网上看到的一些例子,设置似乎是正确的.但是当我实际运行代码并在矢量化开启或关闭之间进行比较时,运行时没有明显的差异.
这是我一直在使用的代码:
#include <string.h>
#include <stdlib.h>
#include <emmintrin.h>
#include <stdio.h>
#include <math.h>
int main(int argc, char** argv) {
long b = strtol(argv[2], NULL, 0);
unsigned long long int i;
unsigned long long int n = (int)pow(2,29);
float total = 0;
float *__restrict__ x1;
float *__restrict__ y1;
posix_memalign((void *)&x1, 16, sizeof(float)*n);
posix_memalign((void *)&y1, 16, sizeof(float)*n);
float *__restrict__ x = __builtin_assume_aligned(x1,16);
float *__restrict__ y = __builtin_assume_aligned(y1,16);
for (i=0;i<n;i++) {
x[i] = i;
y[i] = i;
}
for (i=0; i<n; i++) { …Run Code Online (Sandbox Code Playgroud) 我有以下C代码.第一部分只是从标准中读入一个复数的矩阵,称为矩阵M.有趣的部分是第二部分.
#include <stdio.h>
#include <complex.h>
#include <stdlib.h>
#include <assert.h>
#include <math.h>
int main() {
int n, m, c, d;
float re, im;
scanf("%d %d", &n, &m);
assert(n==m);
complex float M[n][n];
for(c=0; c<n; c++) {
for(d=0; d<n; d++) {
scanf("%f%fi", &re, &im);
M[c][d] = re + im * I;
}
}
for(c=0; c<n; c++) {
for(d=0; d<n; d++) {
printf("%.2f%+.2fi ", creal(M[c][d]), cimag(M[c][d]));
}
printf("\n");
}
/*
Example:input
2 3
1+2i 2+3i 74-4i
3+4i 4+5i -7-8i
*/
/* Part …Run Code Online (Sandbox Code Playgroud) 对于以下循环,如果我告诉它使用关联数学,例如,GCC将仅对循环进行矢量化-Ofast.
float sumf(float *x)
{
x = (float*)__builtin_assume_aligned(x, 64);
float sum = 0;
for(int i=0; i<2048; i++) sum += x[i];
return sum;
}
Run Code Online (Sandbox Code Playgroud)
这是装配 -Ofast -mavx
sumf(float*):
vxorps %xmm0, %xmm0, %xmm0
leaq 8192(%rdi), %rax
.L2:
vaddps (%rdi), %ymm0, %ymm0
addq $32, %rdi
cmpq %rdi, %rax
jne .L2
vhaddps %ymm0, %ymm0, %ymm0
vhaddps %ymm0, %ymm0, %ymm1
vperm2f128 $1, %ymm1, %ymm1, %ymm0
vaddps %ymm1, %ymm0, %ymm0
vzeroupper
ret
Run Code Online (Sandbox Code Playgroud)
这清楚地表明循环已被矢量化.
但是这个循环也有一个依赖链.为了克服添加的延迟,我需要在x86_64上展开并执行至少三次部分和(不包括Skylake,需要展开八次并使用需要在Haswell和Broadwell上展开10次的FMA指令进行添加) .据我所知,我可以展开循环-funroll-loops.
这是装配-Ofast -mavx -funroll-loops.
sumf(float*):
vxorps …Run Code Online (Sandbox Code Playgroud) 我看到的"问题"只是使用autovectorizer将用户编写的循环代码转换为每次编译的SIMD指令,作为通常优化的一部分,如果你更改编译器,你就不能确定它也会自动矢量化你的代码同样好.
因此,如果您只想要针对单个处理器,我希望编译器为我生成高级C代码,用于特定函数,该函数使用x86内部包装函数,这些函数通用于不同的编译器供应商.
是否有一个Decompiler,或者甚至是GCC的编译器选项给我这个代码?
我有一个简单的循环,取 n 个复数的乘积。当我执行这个循环数百万次时,我希望它尽可能快。我知道可以使用 SSE3 和 gcc 内在函数快速完成此操作_mm_addsub_ps,但我感兴趣的是是否可以让 gcc 自动向量化这样的代码,即复数的乘积:
#include <complex.h>
complex float f(complex float x[], int n ) {
complex float p = 1.0;
for (int i = 0; i < n; i++)
p *= x[i];
return p;
}
Run Code Online (Sandbox Code Playgroud)
您获得的程序集gcc -S -O3 -ffast-math是:
.file "test.c"
.section .text.unlikely,"ax",@progbits
.LCOLDB2:
.text
.LHOTB2:
.p2align 4,,15
.globl f
.type f, @function
f:
.LFB0:
.cfi_startproc
testl %esi, %esi
jle .L4
leal -1(%rsi), %eax
pxor %xmm2, %xmm2
movss .LC1(%rip), %xmm3
leaq …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) 我有一个过滤器m_f作用于输入向量v通过
Real d2v = m_f[0]*v[i];
for (size_t j = 1; j < m_f.size(); ++j)
{
d2v += m_f[j] * (v[i + j] + v[i - j]);
}
Run Code Online (Sandbox Code Playgroud)
perf 告诉我们这个循环在哪里热:
该vaddpd和vfma231pd意义; 没有它们,我们肯定无法执行此操作.但缓慢vpermpd让我感到困惑.它完成了什么?
我正在尝试将此 for 循环向量化。使用 Rpass 标志后,我收到以下评论:
int someOuterVariable = 0;
for (unsigned int i = 7; i != -1; i--)
{
array[someOuterVariable + i] -= 0.3 * anotherArray[i];
}
Remark:
The cost-model indicates that vectorization is not beneficial
the cost-model indicates that interleaving is not beneficial
Run Code Online (Sandbox Code Playgroud)
我想了解这意味着什么。“交错不是有益的”是否意味着数组索引不正确?
我试图使用vmap基于 JAX 文档的最小工作示例来了解 JAX 的自动矢量化功能。
我不明白如何in_axes正确使用。在下面的示例中,我可以设置in_axes=(None, 0)或in_axes=(None, 1)导致相同的结果。为什么会这样?
为什么我必须使用in_axes=(None, 0)而不是类似的东西in_axes=(0, )?
import jax.numpy as jnp
from jax import vmap
def predict(params, input_vec):
assert input_vec.ndim == 1
activations = input_vec
for W, b in params:
outputs = jnp.dot(W, activations) + b
activations = jnp.tanh(outputs)
return outputs
if __name__ == "__main__":
# Parameters
dims = [2, 3, 5]
input_dims = dims[0]
batch_size = 2
# Weights
params = list()
for …Run Code Online (Sandbox Code Playgroud)