- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
根据https://sourceware.org/glibc/wiki/libmvec GCC 具有数学函数的向量实现。它们可以被编译器用于优化,可以在这个例子中看到:https://godbolt.org/g/IcxtVi , 编译器使用一些重整的正弦函数并且一次操作 4 个 double
我知道如果我需要数学函数可以使用 SIMD 数学库,但我仍然感兴趣是否有一种方法可以手动调用 GCC 中已经存在的向量化数学函数 __m256d
具有某种内在或任何其他方式的变量?
最佳答案
Libmvec 是一个 x86_64 glibc 库,具有 SSE4、AVX、AVX2 和 AVX-512 矢量化函数cos
, exp
, log
, sin
, pow
, 和 sincos
,单精度和 double 。
这些函数的精度为 4-ulp 最大相对误差。
通常 gcc 会插入对 Libmvec 函数的调用,例如 _ZGVdN4v_cos
,
虽然它是自动矢量化标量代码,例如 -ffast-math
或者#pragma omp simd
.然而,这些向量函数也适用于
手动矢量化 C 代码。
不幸的是 Libmvec document
没有给出很多例子来解释如何使用这些函数
手动矢量化 C 代码。
要使用 Libmvec 函数,必须包含函数声明
并将代码与 -lm
链接(与数学库链接)选项。
链接器不需要 -lmvec
,链接标准数学库-lm
是
足以用于非静态构建,请参阅 here .-ffast-math
编译器选项不是必需的。
以下示例显示如何调用 AVX2 矢量化函数。
此示例在标准 Ubuntu 18.04 系统(glibc 版本 2.27)上成功运行。
/* gcc -Wall -O3 -m64 -march=skylake libmvec_ex.c -lm */
#include <immintrin.h>
#include <stdio.h>
#include <stdint.h>
#define _GNU_SOURCE /* These two lines are optional. They are only needed to use the scalar */
#include <math.h> /* functions sin, exp, sincos,... */
__m256d _ZGVdN4v_cos(__m256d x);
__m256d _ZGVdN4v_exp(__m256d x);
__m256d _ZGVdN4v_log(__m256d x);
__m256d _ZGVdN4v_sin(__m256d x);
__m256d _ZGVdN4vv_pow(__m256d x, __m256d y);
void _ZGVdN4vvv_sincos(__m256d x, __m256i ptrs, __m256i ptrc);
__m256 _ZGVdN8v_cosf(__m256 x);
__m256 _ZGVdN8v_expf(__m256 x);
__m256 _ZGVdN8v_logf(__m256 x);
__m256 _ZGVdN8v_sinf(__m256 x);
__m256 _ZGVdN8vv_powf(__m256 x, __m256 y);
void _ZGVdN8vvv_sincosf(__m256 x, __m256i ptrs_lo, __m256i ptrs_hi, __m256i ptrc_lo, __m256i ptrc_hi);
int mm256_print_pd(__m256d x);
int mm256_print_ps(__m256 x);
int main()
{
__m256d x, y, z;
__m256 xf, yf, zf;
double z_s[4], z_c[4]; /* sincos output */
float zf_s[8], zf_c[8]; /* sincosf output */
__m256i ptrs, ptrc; /* Pointers to the elements of z_s and z_c */
__m256i ptrs_lo, ptrs_hi, ptrc_lo, ptrc_hi;
x = _mm256_set_pd (0.04, 0.03, 0.02, 0.01);
y = _mm256_set_pd (2.0, 1.5, 1.0, 0.5);
xf = _mm256_set_ps (0.08f, 0.07f, 0.06f, 0.05f, 0.04f, 0.03f, 0.02f, 0.01f);
yf = _mm256_set_ps (4.0f, 3.5f, 3.0f, 2.5f, 2.0f, 1.5f, 1.0f, 0.5f);
printf("AVX2 Double precision examples\n");
printf("x "); mm256_print_pd(x);
printf("y "); mm256_print_pd(y);
z =_ZGVdN4v_cos(x); printf("cos(x) "); mm256_print_pd(z);
z =_ZGVdN4v_exp(x); printf("exp(x) "); mm256_print_pd(z);
z =_ZGVdN4v_log(x); printf("log(x) "); mm256_print_pd(z);
z =_ZGVdN4v_sin(x); printf("sin(x) "); mm256_print_pd(z);
z =_ZGVdN4vv_pow(x, y); printf("pow(x,y) "); mm256_print_pd(z);
ptrs = _mm256_set_epi64x((uint64_t)&z_s[3],(uint64_t)&z_s[2],(uint64_t)&z_s[1],(uint64_t)&z_s[0]);
ptrc = _mm256_set_epi64x((uint64_t)&z_c[3],(uint64_t)&z_c[2],(uint64_t)&z_c[1],(uint64_t)&z_c[0]);
/* Alternative: ptrs = _mm256_add_epi64(_mm256_set1_epi64x((uint64_t)&z_s[0]),_mm256_set_epi64x(24,16,8,0)); */
/* This might be more efficient if the destination addresses are contiguous in memory. */
_ZGVdN4vvv_sincos(x, ptrs, ptrc); /* The results of _ZGVdN4vvv_sincos are scattered into the adresses in ptrs and ptrc */
printf("sincos cos(x) %12.8f %12.8f %12.8f %12.8f \n", z_c[3], z_c[2], z_c[1], z_c[0]);
printf("sincos sin(x) %12.8f %12.8f %12.8f %12.8f\n\n", z_s[3], z_s[2], z_s[1], z_s[0]);
printf("AVX2 Single precision examples\n");
printf("x "); mm256_print_ps(xf);
printf("y "); mm256_print_ps(yf);
zf =_ZGVdN8v_cosf(xf); printf("cosf(x) "); mm256_print_ps(zf);
zf =_ZGVdN8v_expf(xf); printf("expf(x) "); mm256_print_ps(zf);
zf =_ZGVdN8v_logf(xf); printf("logf(x) "); mm256_print_ps(zf);
zf =_ZGVdN8v_sinf(xf); printf("sinf(x) "); mm256_print_ps(zf);
zf =_ZGVdN8vv_powf(xf, yf);printf("powf(x,y) "); mm256_print_ps(zf);
ptrs_lo = _mm256_set_epi64x((uint64_t)&zf_s[3],(uint64_t)&zf_s[2],(uint64_t)&zf_s[1],(uint64_t)&zf_s[0]);
ptrs_hi = _mm256_set_epi64x((uint64_t)&zf_s[7],(uint64_t)&zf_s[6],(uint64_t)&zf_s[5],(uint64_t)&zf_s[4]);
ptrc_lo = _mm256_set_epi64x((uint64_t)&zf_c[3],(uint64_t)&zf_c[2],(uint64_t)&zf_c[1],(uint64_t)&zf_c[0]);
ptrc_hi = _mm256_set_epi64x((uint64_t)&zf_c[7],(uint64_t)&zf_c[6],(uint64_t)&zf_c[5],(uint64_t)&zf_c[4]);
_ZGVdN8vvv_sincosf(xf, ptrs_lo, ptrs_hi, ptrc_lo, ptrc_hi); /* The results of _ZGVdN8vvv_sincosf are scattered to the adresses in ptrs_lo, ptrs_hi, ptrc_lo, and ptrc_hi */
printf("sincosf cos(x)%12.8f %12.8f %12.8f %12.8f %12.8f %12.8f %12.8f %12.8f \n",
zf_c[7], zf_c[6], zf_c[5], zf_c[4], zf_c[3], zf_c[2], zf_c[1], zf_c[0]);
printf("sincosf sin(x)%12.8f %12.8f %12.8f %12.8f %12.8f %12.8f %12.8f %12.8f \n",
zf_s[7], zf_s[6], zf_s[5], zf_s[4], zf_s[3], zf_s[2], zf_s[1], zf_s[0]);
return 0;
}
__attribute__ ((noinline)) int mm256_print_pd(__m256d x){
double vec_x[4];
_mm256_storeu_pd(vec_x,x);
printf("%12.8f %12.8f %12.8f %12.8f \n", vec_x[3], vec_x[2], vec_x[1], vec_x[0]);
return 0;
}
__attribute__ ((noinline)) int mm256_print_ps(__m256 x){
float vec_x[8];
_mm256_storeu_ps(vec_x,x);
printf("%12.8f %12.8f %12.8f %12.8f %12.8f %12.8f %12.8f %12.8f \n", vec_x[7], vec_x[6], vec_x[5], vec_x[4],
vec_x[3], vec_x[2], vec_x[1], vec_x[0]);
return 0;
}
/*
$ ./a.out
AVX2 Double precision examples
x 0.04000000 0.03000000 0.02000000 0.01000000
y 2.00000000 1.50000000 1.00000000 0.50000000
cos(x) 0.99920011 0.99955003 0.99980001 0.99995000
exp(x) 1.04081077 1.03045453 1.02020134 1.01005017
log(x) -3.21887582 -3.50655790 -3.91202301 -4.60517019
sin(x) 0.03998933 0.02999550 0.01999867 0.00999983
pow(x,y) 0.00160000 0.00519615 0.02000000 0.10000000
sincos cos(x) 0.99920011 0.99955003 0.99980001 0.99995000
sincos sin(x) 0.03998933 0.02999550 0.01999867 0.00999983
AVX2 Single precision examples
x 0.08000000 0.07000000 0.06000000 0.05000000 0.04000000 0.03000000 0.02000000 0.01000000
y 4.00000000 3.50000000 3.00000000 2.50000000 2.00000000 1.50000000 1.00000000 0.50000000
cosf(x) 0.99680173 0.99755102 0.99820060 0.99875027 0.99920011 0.99955004 0.99979997 0.99995005
expf(x) 1.08328700 1.07250810 1.06183648 1.05127108 1.04081070 1.03045452 1.02020133 1.01005018
logf(x) -2.52572870 -2.65926003 -2.81341076 -2.99573231 -3.21887589 -3.50655794 -3.91202307 -4.60517025
sinf(x) 0.07991469 0.06994285 0.05996400 0.04997917 0.03998933 0.02999550 0.01999867 0.00999983
powf(x,y) 0.00004096 0.00009075 0.00021600 0.00055902 0.00160000 0.00519615 0.02000000 0.10000000
sincosf cos(x) 0.99680173 0.99755102 0.99820060 0.99875027 0.99920011 0.99955004 0.99979997 0.99995005
sincosf sin(x) 0.07991469 0.06994285 0.05996400 0.04997917 0.03998933 0.02999550 0.01999867 0.00999983
sincos
函数将结果分散到 4(双)或
/* Double precision Libmvec Intel SVML function */
/* SSE4 */
__m128d _ZGVbN2v_cos(__m128d x); /* _mm_cos_pd(x) */
__m128d _ZGVbN2v_exp(__m128d x); /* _mm_exp_pd(x) */
__m128d _ZGVbN2v_log(__m128d x); /* _mm_log_pd(x) */
__m128d _ZGVbN2v_sin(__m128d x); /* _mm_sin_pd(x) */
__m128d _ZGVbN2vv_pow(__m128d x, __m128d y); /* _mm_pow_pd(x, y) */
void _ZGVbN2vvv_sincos(__m128d x, __m128i ptrs, __m128i ptrc); /* _mm_sincos_pd */
/* AVX */
__m256d _ZGVcN4v_cos(__m256d x); /* _mm256_cos_pd(x) */
__m256d _ZGVcN4v_exp(__m256d x); /* _mm256_exp_pd(x) */
__m256d _ZGVcN4v_log(__m256d x); /* _mm256_log_pd(x) */
__m256d _ZGVcN4v_sin(__m256d x); /* _mm256_sin_pd(x) */
__m256d _ZGVcN4vv_pow(__m256d x, __m256d y); /* _mm256_pow_pd(x, y) */
void _ZGVcN4vvv_sincos(__m256d x, __m256i ptrs, __m256i ptrc); /* _mm256_sincos_pd */
/* AVX2 */
__m256d _ZGVdN4v_cos(__m256d x); /* _mm256_cos_pd(x) */
__m256d _ZGVdN4v_exp(__m256d x); /* _mm256_exp_pd(x) */
__m256d _ZGVdN4v_log(__m256d x); /* _mm256_log_pd(x) */
__m256d _ZGVdN4v_sin(__m256d x); /* _mm256_sin_pd(x) */
__m256d _ZGVdN4vv_pow(__m256d x, __m256d y); /* _mm256_pow_pd(x, y) */
void _ZGVdN4vvv_sincos(__m256d x, __m256i ptrs, __m256i ptrc); /* _mm256_sincos_pd */
/* Single precision Libmvec Intel SVML function */
/* SSE4 */
__m128 _ZGVbN4v_cosf(__m128 x); /* _mm_cos_ps(x) */
__m128 _ZGVbN4v_expf(__m128 x); /* _mm_exp_ps(x) */
__m128 _ZGVbN4v_logf(__m128 x); /* _mm_log_ps(x) */
__m128 _ZGVbN4v_sinf(__m128 x); /* _mm_sin_ps(x) */
__m128 _ZGVbN4vv_powf(__m128 x, __m128 y); /* _mm_pow_ps(x, y) */
void _ZGVbN4vvv_sincosf(__m128 x, __m128i ptrs_lo, __m128i ptrs_hi, __m128i ptrc_lo, __m128i ptrc_hi); /* _mm_sincos_ps */
/* AVX */
__m256 _ZGVcN8v_cosf(__m256 x); /* _mm256_cos_ps(x) */
__m256 _ZGVcN8v_expf(__m256 x); /* _mm256_exp_ps(x) */
__m256 _ZGVcN8v_logf(__m256 x); /* _mm256_log_ps(x) */
__m256 _ZGVcN8v_sinf(__m256 x); /* _mm256_sin_ps(x) */
__m256 _ZGVcN8vv_powf(__m256 x, __m256 y); /* _mm256_pow_ps(x, y) */
void _ZGVcN8vvv_sincosf(__m256 x, __m256i ptrs_lo, __m256i ptrs_hi, __m256i ptrc_lo, __m256i ptrc_hi); /* _mm256_sincos_ps */
/* AVX2 */
__m256 _ZGVdN8v_cosf(__m256 x); /* _mm256_cos_ps(x) */
__m256 _ZGVdN8v_expf(__m256 x); /* _mm256_exp_ps(x) */
__m256 _ZGVdN8v_logf(__m256 x); /* _mm256_log_ps(x) */
__m256 _ZGVdN8v_sinf(__m256 x); /* _mm256_sin_ps(x) */
__m256 _ZGVdN8vv_powf(__m256 x, __m256 y); /* _mm256_pow_ps(x, y) */
void _ZGVdN8vvv_sincosf(__m256 x, __m256i ptrs_lo, __m256i ptrs_hi, __m256i ptrc_lo, __m256i ptrc_hi); /* _mm256_sincos_ps */
/* Note that I only tested the AVX2 function declarations. */
/* AVX-512: _ZGVeN16v_cosf, _ZGVeN8v_cos etc. */
关于gcc - SIMD 寄存器的数学函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40475140/
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 想改进这个问题?将问题更新为 on-topic对于堆栈溢出。 7年前关闭。 Improve this qu
我有一个代码库,我可以在我的 mac 上编译和运行,但不能在我的远程 linux 机器上编译和运行,我不确定为什么。 编译时出现错误 fatal error: simd/simd.h: No such
我需要了解如何编写一些可并行化问题的 C++ 跨平台实现,以便在可用的情况下利用 SIMD(SSE、SPU 等)。以及我希望能够在运行时在 SIMD 和非 SIMD 之间切换。 您建议我如何解决这个问
我正在使用 AVX 内在 _mm256_extract_epi32() . 不过,我不完全确定我是否正确使用它,因为 gcc 不喜欢我的代码,而 clang 编译它并运行它没有问题。 我根据整数变量的
当我可以使用 SSE3 或 AVX 时,SSE2 或 MMX 等较旧的 SSE 版本是否可用 - 还是我还需要单独检查它们? 最佳答案 一般来说,这些都是附加的,但请记住,多年来英特尔和 AMD 对这
在 godbolt.org 使用 gcc 7.2 我可以看到以下内容 code在汇编程序中翻译得非常好。我看到 1 次加载、1 次添加和 1 次存储。 #include __attribute__(
假设我们有一个函数将两个数组相乘,每个数组有 1000000 个 double 值。在 C/C++ 中,该函数如下所示: void mul_c(double* a, double* b) {
我有一个 A = a1 a2 a3 a4 b1 b2 b3 b4 c1 c2 c3 c4 d1 d2 d3 d4 我有两排, float32x2_t a = a1 a2 flo
我正在考虑编写一个 SIMD vector 数学库,因此作为一个快速基准,我编写了一个程序,该程序执行 1 亿(4 个 float ) vector 元素乘法并将它们加到累积总数中。对于我的经典非 S
我正在开发带有英特尔编译器 OpenMP 4.0 的英特尔 E5(6 核、12 线程) 为什么这段代码 SIMD 编译比并行 SIMD 编译更快? for (int suppv = 0; suppv
OpenMP 4.0 引入了 SIMD 结构以利用 CPU 的 SIMD 指令。根据规范http://www.openmp.org/mp-documents/OpenMP4.0.0.pdf ,有两种结
英特尔编译器允许我们通过以下方式对循环进行矢量化 #pragma simd for ( ... ) 但是,您也可以选择使用 OpenMP 4 的指令执行此操作: #pragma omp simd fo
关注我的 x86 question ,我想知道如何在 Arm-v8 上有效地矢量化以下代码: static inline uint64_t Compress8x7bit(uint64_t x) {
Intel 提供了几个 SIMD 命令,它们似乎都对 128 位数据执行按位异或: _mm_xor_pd(__m128d, __m128d) _mm_xor_ps(__m128, __m128) _m
可以使用“位打包”技术压缩无符号整数:在一个无符号整数 block 中,只存储有效位,当一个 block 中的所有整数都“小”时,会导致数据压缩。该方法称为 FOR (引用框架)。 有SIMD lib
SSE 寄存器是否在逻辑处理器(超线程)之间共享或复制? 对于 SSE 繁重的程序,我能否期望从并行化中获得与普通程序相同的加速(英特尔声称具有超线程的处理器为 30%)? 最佳答案 从英特尔的文档中
我正在编写一个使用 SSE 指令来乘法和相加整数值的程序。我用浮点数做了同样的程序,但我的整数版本缺少一个指令。 使用浮点数,在完成所有操作后,我将 de 值返回到常规浮点数数组,执行以下操作: _m
我正在开发基于Intel指令集(AVX,FMA等)的高性能算法。当数据按顺序存储时,我的算法(内核)运行良好。但是,现在我面临一个大问题,但没有找到解决方法或解决方案: see 2D Matrix i
大家好 :) 我正在尝试了解有关浮点、SIMD/数学内在函数和 gcc 的快速数学标志的一些概念。更具体地说,我在 x86 cpu 上使用 MinGW 和 gcc v4.5.0。 我已经搜索了一段时间
根据https://sourceware.org/glibc/wiki/libmvec GCC 具有数学函数的向量实现。它们可以被编译器用于优化,可以在这个例子中看到:https://godbolt.
我是一名优秀的程序员,十分优秀!