- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在对两个数组求和并输出第三个数组(不是缩减)。像这样:
void add_scalar(float* result, const float* a, const float* b, const int N) {
for(int i = 0; i<N; i++) {
result[i] = a[i] + b[i];
}
}
我想以最大的吞吐量来做这件事。对于 SSE 和四核,我天真地期望速度提高 16 倍(SSE 为四倍,四核为四倍)。我已经用 SSE(和 AVX)实现了代码。 Visual Studio 2012 具有自动矢量化功能,但我通过“展开循环”获得了更好的结果。我为四种大小的数组(32 字节对齐)运行我的代码:小于 32KB、小于 256KB、小于 8MB 和大于 8MB,对应于 L1、L2、L3 缓存和主内存。对于 L1,我看到使用展开的 SSE 代码(使用 AVX 时为 5-6)大约有 4 倍的加速。这和我期望的一样多。之后每个缓存级别的效率都会下降。然后我使用 OpenMP 在每个内核上运行。我在数组的主循环之前放置了“#pragma omp parallel for”。但是,我获得的最佳加速是 SSE + OpenMP 的 5-6 倍。有谁知道为什么我没有看到 16 倍的加速?也许是由于数组从系统内存到缓存的一些“上传”时间?我意识到我应该分析代码,但这本身就是我必须学习的另一次冒险。
#define ROUND_DOWN(x, s) ((x) & ~((s)-1))
void add_vector(float* result, const float* a, const float* b, const int N) {
__m128 a4;
__m128 b4;
__m128 sum;
int i = 0;
for(; i < ROUND_DOWN(N, 8); i+=8) {
a4 = _mm_load_ps(a + i);
b4 = _mm_load_ps(b + i);
sum = _mm_add_ps(a4, b4);
_mm_store_ps(result + i, sum);
a4 = _mm_load_ps(a + i + 4);
b4 = _mm_load_ps(b + i + 4);
sum = _mm_add_ps(a4, b4);
_mm_store_ps(result + i + 4, sum);
}
for(; i < N; i++) {
result[i] = a[i] + b[i];
}
return 0;
}
我的错误主循环有这样的竞争条件:
float *a = (float*)_aligned_malloc(N*sizeof(float), 32);
float *b = (float*)_aligned_malloc(N*sizeof(float), 32);
float *c = (float*)_aligned_malloc(N*sizeof(float), 32);
#pragma omp parallel for
for(int n=0; n<M; n++) { //M is an integer of the number of times to run over the array
add_vector(c, a, b, N);
}
我根据 Grizzly 的建议更正了主循环:
for(int i=0; i<4; i++) {
results[i] = (float*)_aligned_malloc(N*sizeof(float), 32);
}
#pragma omp parallel for num_threads(4)
for(int t=0; t<4; t++) {
for(int n=0; n<M/4; n++) { //M is an integer of the number of times to run over the array
add_vector(results[t], a, b, N);
}
}
最佳答案
免责声明:和你一样,我没有分析代码,所以我不能绝对肯定地回答。
您的问题很可能与内存带宽或并行化开销有关。
你的循环是非常轻计算的,因为它对 3 个内存操作执行 1 个加法运算,这使你自然受到内存带宽的限制(考虑到 ALU 吞吐量比现代架构中的内存带宽好得多)。因此,您的大部分时间都花在了传输数据上。
如果数据小到足以容纳缓存,您可以(理论上)将 openmp 线程绑定(bind)到特定内核并确保 vector 的正确部分位于特定内核的 L1/L2 缓存中,但这不会真的很有帮助,除非你可以并行初始化(当你传输数据时并不重要,如果你必须这样做的话)。因此,将数据从一个核心缓存传输到另一个核心缓存会受到影响。
如果数据不适合处理器缓存,您最终会受到主内存带宽的限制。由于预取,一个内核可能几乎可以用尽这种简单访问模式的带宽,让您的增长空间很小。
要记住的第二点是,创建 omp parallel
构造和分配循环会产生一定的开销。对于小型数据集(适合 L1/L2/L3 的数据集可能符合条件),这种开销很容易与计算时间本身一样高,几乎没有加速。
关于c - SSE/AVX + OpenMP : fast sum of arrays,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15271800/
我正在尝试优化一些矩阵计算,我想知道是否可以在编译时检测 SSE/SSE2/AVX/AVX2/AVX-512/AVX-128-FMA/KCVI[ 1] 是否由编译器启用?非常适合 GCC 和 Clan
我想仅使用avx而不是avx2来实现64位转置操作。它应该这样做: // in = Hh Hl Lh Ll // | X | // out = Hh Lh Hl Ll 这就是使
如果我有一个 AVX 寄存器,里面有 4 个 double 值,我想将它的反向存储在另一个寄存器中,是否可以用一个内部命令来实现? 例如:如果我在 SSE 寄存器中有 4 个 float ,我可以使用
最初我试图重现 Agner Fog 的微体系结构指南部分“YMM 和 ZMM 向量指令的预热期”中描述的效果,它说: The processor turns off the upper parts o
我的 C++ 代码使用 SSE,现在我想改进它以支持 AVX(当它可用时)。因此,我检测 AVX 何时可用并调用使用 AVX 命令的函数。我使用 Win7 SP1 + VS2010 SP1 和带有 A
我有一大块内存,比如说 256 KiB 或更长。我想计算整个 block 中 1 位的数量,或者换句话说:将所有字节的“总体计数”值相加。 我知道 AVX-512 有一个 VPOPCNTDQ inst
有多快 tensorflow-gpu与没有 AVX 和 AVX2 相比,有 AVX 和 AVX2 吗? 我试图使用谷歌找到答案,但没有成功。很难重新编译tensorflow-gpu对于 Windows
为什么avx sqrt(非压缩)指令有三个操作数? vsqrtsd xmm1, xmm2, xmm3 这是否意味着类似于 xmm1=xmm2=sqrt(xmm3)? 编辑:下面的详细答案但总之流水线的
我正在研究Intel intrinsics guide的展开和压缩操作。我对这两个概念感到困惑: 对于__m128d _mm_mask_expand_pd (__m128d src, __mmask8
我在 Intel Intrinsic Guide v2.7 中找不到它们。您知道 AVX 或 AVX2 指令集是否支持它们吗? 最佳答案 原始 AVX 指令集中没有分散或收集指令。 AVX2 添加了收
我正在尝试将函数转换为 AVX 版本。函数本身基本上只是比较浮点数并返回真/假取决于计算。 这是原始函数: bool testSingle(float* thisFloat, float* other
我遇到了 AVX 内部指令 _mm256_testc_pd() 的一个非常奇怪的行为。在这里你可以看到这个功能的描述 https://software.intel.com/sites/landingp
我有一个 256 位 AVX 寄存器,其中包含 4 个单精度复数,存储为实数、虚数、实数、虚数等。我目前正在将整个 256 位寄存器写回内存并在那里求和,但这似乎效率低下. 如何使用 AVX(或 AV
#include "stdio.h" #include "math.h" #include "stdlib.h" #include "x86intrin.h" void dd_m(double *cl
有没有办法对 AVX 寄存器进行水平异或——特别是对 256 位寄存器的四个 64 位组件进行异或? 目标是获得 AVX 寄存器的所有 4 个 64 位组件的异或。它本质上与水平添加( _mm256_
当我尝试使用 AVX 获取数据时,出现运行时错误 - 段错误: int i = 0; const int sz = 9; size_t *src1 = (size_t *)_mm_malloc(sz*
当我尝试使用 AVX 展开最简单的循环时,出现运行时错误 - 段错误: const int sz = 9; float *src = (float *)_mm_malloc(sz*
我想将两个 256 位 vector (__m256d) 合并为一个 256位 vector ,通过省略每个 64 位 double 的上半部分。 所以,如果在下面,a_i, b_i, ... 是 3
我测试了以下简单的功能 void mul(double *a, double *b) { for (int i = 0; i #include #include #include #defi
_mm_i32gather_epi32() 的当前英特尔内在函数指南将每个子词的计算地址描述为: addr := base_addr + SignExtend64(vindex[m+31:m]) *
我是一名优秀的程序员,十分优秀!