我在这里做错了什么?我得到4个零,而不是:
2
4
6
8
我也很想修改我的. ami函数,以便通过更长的向量运行,因为为了在这里进行半衰期,我刚刚使用了一个具有四个元素的向量,这样我就可以在没有SIMD 256位寄存器循环的情况下对该向量进行求和。
#include <iostream>
#include <chrono>
extern "C" double *addVec(double *C, double *A, double *B, size_t &N);
int main()
{
size_t N = 1 << 2;
size_t reductions = N / 4;
double *A = (double*)_aligned_malloc(N*sizeof(double), 32);
double *B = (double*)_aligned_malloc(N*sizeof(double), 32);
double *C = (double*)_aligned_malloc(N*sizeof(double), 32);
for (size_t i = 0; i < N; i++)
{
A[i] = double(i + 1);
B[i] = double(i + 1);
}
auto start = std::chrono::high_resolution_clock::now();
double *out = addVec(C, A, B, reductions);
auto finish = std::chrono::high_resolution_clock::now();
for (size_t i = 0; i < N; i++)
{
std::cout << out[i] << std::endl;
}
std::cout << "\n\n";
std::cout << std::chrono::duration_cast<std::chrono::nanoseconds>(finish - start).count() << " ns\n";
std::cin.get();
_aligned_free(A);
_aligned_free(B);
_aligned_free(C);
return 0;
}
.data
; C -> RCX
; A -> RDX
; B -> r8
; N -> r9
.code
addVec proc
;xor rbx, rbx
align 16
;aIn:
vmovapd ymm0, ymmword ptr [rdx]
;vmovapd ymm1, ymmword ptr [rdx + rbx + 4]
vmovapd ymm2, ymmword ptr [r8]
;vmovapd ymm3, ymmword ptr [r8 + rbx + 4]
vaddpd ymm0, ymm2, ymm3
vmovapd ymmword ptr [rcx], ymm3
;inc rbx
;cmp rbx, qword ptr [r9]
;jl aIn
mov rax, rcx ; return the address of the output vector
ret
addVec endp
end
我还想得到一些其他的澄清:
> < li >我的CPU的每个内核有八个256位寄存器(ymm0-ymm7)还是总共有八个? < li >所有其他寄存器,如rax、rbx等...是总计还是每个核心? < li >由于我仅用SIMD协处理器和一个内核就可以每周期处理4个double,我能否用CPU的其余部分每周期执行另一条指令?例如,我可以用一个内核在每个周期添加5个double吗?(4同1) < li>
如果我没有在我的汇编函数中放入一个循环,而执行了如下操作,该怎么办?:
#pragma openmp并行
用于(size_t i=0;i
addVec(C i,A i,B i)
这是否会分叉coreNumber个超线程线程,并且每个线程在四个double上执行SIMD加法?所以每个周期总共4 * coreNumber double?我不能在这里添加超线程,对吗?
更新我可以这样做吗?:
.data
;// C -> RCX
;// A -> RDX
;// B -> r8
.code
addVec proc
; One cycle 8 micro-op
vmovapd ymm0, ymmword ptr [rdx] ; 1 port
vmovapd ymm1, ymmword ptr [rdx + 32]; 1 port
vmovapd ymm2, ymmword ptr [r8] ; 1 port
vmovapd ymm3, ymmword ptr [r8 + 32] ; 1 port
vfmadd231pd ymm0, ymm2, ymm4 ; 1 port
vfmadd231pd ymm1, ymm3, ymm4 ; 1 port
vmovapd ymmword ptr [rcx], ymm0 ; 1 port
vmovapd ymmword ptr [rcx + 32], ymm1; 1 port
; Return the address of the output vector
mov rax, rcx ; 1 port ?
ret
addVec endp
end
或者只是这个,因为我会超过你告诉我的六个端口?
.data
;// C -> RCX
;// A -> RDX
;// B -> r8
.code
addVec proc
;align 16
; One cycle 5 micro-op ?
vmovapd ymm0, ymmword ptr [rdx] ; 1 port
vmovapd ymm1, ymmword ptr [r8] ; 1 port
vfmadd231pd ymm0, ymm1, ymm2 ; 1 port
vmovapd ymmword ptr [rcx], ymm0 ; 1 port
; Return the address of the output vector
mov rax, rcx ; 1 port ?
ret
addVec endp
end
代码得到错误结果的原因是程序集中的语法向后。
您使用的是英特尔语法,其中目标应位于源之前。因此,在原始的 .asm 代码中,您应该更改
vaddpd ymm0, ymm2, ymm3
到
vaddpd ymm3, ymm2, ymm0
了解这一点的一种方法是使用内部函数,然后查看反汇编。
extern "C" double *addVec(double * __restrict C, double * __restrict A, double * __restrict B, size_t &N) {
__m256d x = _mm256_load_pd((const double*)A);
__m256d y = _mm256_load_pd((const double*)B);
__m256d z = _mm256_add_pd(x,y);
_mm256_store_pd((double*)C, z);
return C;
}
在 Linux 上使用 g -S -O3 -mavx -masm=intel -mabi=ms foo 从
GCC 中分离出来.cpp给出:
vmovapd ymm0, YMMWORD PTR [rdx]
mov rax, rcx
vaddpd ymm0, ymm0, YMMWORD PTR [r8]
vmovapd YMMWORD PTR [rcx], ymm0
vzeroupper
ret
< code>vaddpd ymm0,ymm0,YMMWORD PTR [rdx]指令将加载和加法运算融合到一个融合的微操作中。当我对您的代码使用该函数时,它会得到2,4,6,8。
您可以找到将两个数组 x
和 y
求和的源代码,并将其写出到 l1-内存带宽-50-效率下降-使用地址(差异为 4096)的数组 z
。这使用内部函数和展开八次。使用 gcc -S
或 objdump -d
对代码进行反化。另一个做几乎相同的事情并在汇编中编写的源是获取峰值带宽 -haswell-in-the-l1-cache-only-getting-62。在文件triad_fma_asm.asm
中,将行 pi: dd 3.14159
更改为 pi: dd 1.0
。这两个示例都使用单个浮点,因此,如果您想要双精度,则必须进行必要的更改。
其他问题的答案如下:
请注意,每个核心的寄存器远远多于您可以直接编程的逻辑寄存器。
见 1.以上
自2006年以来,通过Haswell的Core2处理器每个时钟最多可以处理4µop。然而,使用两种称为微运算融合和宏运算融合的技术,可以使用Haswell实现每个时钟周期六个微运算。
例如,微操作融合可以将负载和加成一个所谓的融合微操作,但每个微操作仍然需要自己的端口。宏观操作融合可以融合,例如标量加法和跳转到一个只需要一个端口的微操作。宏观操作融合本质上是二对一。
Haswell有八个端口。使用这样的七个端口,您可以在一个时钟周期内获得六个微操作。
256-load + 256-FMA //one fused µop using two ports
256-load + 256-FMA //one fused µop using two ports
256-store //one µop using two ports
64-bit add + jump //one µop using one port
因此,实际上Haswell的每个内核在一个时钟周期内可以处理16个双精度浮点运算(每个FMA处理4个乘法和4个加法)、2个256位加载、1个256位存储和1个64位加法和分支。在这个问题中,获得-peak-bandwidth-on-has-well-in-the-L1-cache-only-getting-62,我使用六个端口在一个时钟周期内获得(理论上)五个微操作。然而,实际上在Haswell上这是很难实现的。
对于读取两个数组并写入一个数组的特定操作,它受每个时钟周期两次读取的约束,因此每个时钟周期只能发出一个FMA。所以它最多只能在每个时钟周期进行四次双精度运算。
但让我告诉你一个小秘密,英特尔不想让人们谈论太多。大多数操作都受内存带宽限制,无法从并行化中受益。这包括您问题中的操作。因此,尽管英特尔每隔几年就推出新技术(例如,AVX、FMA、AVX512、双倍的核心数量),每次都会使性能翻倍,声称摩尔定律在实践中得到了应用,但平均收益是线性的,而不是指数的,而且几年来一直如此。
问题内容: 有人可以告诉我如何摆脱BeanCreationException吗? 在向Owner.java中添加两个变量之后,我得到了BeanCreationException,如下所示: 我还添加了用于猫和狗的getter和setter方法,以及将猫和狗作为宠物子集填充的方法,如下所示: 当我在Eclipse中在服务器上运行方式为…运行时,应用程序无法初始化,并显示以下错误消息: 这是busin
问题内容: 我正在通过“ Python进行数据分析”,但是我不了解特定的功能。添加两个熊猫系列对象将自动对齐索引数据,但是如果一个对象不包含该索引,则将其作为NaN返回。例如从书中: 结果: 当我将它们加在一起时,我得到了… 那么,为什么犹他州的价值是NaN而不是500?看来500 + NaN = 500。是什么赋予了?我缺少什么,请解释。 更新: 问题答案: 熊猫不假定500 + NaN = 5
问题内容: 我经常对Python列表进行矢量加法。 示例:我有两个这样的列表: 我现在想将b添加到a以获得结果。 通常我最终会这样: 有没有什么有效的,标准的方法可以减少打字? 更新:可以假定列表的长度为3,并且包含浮点数。 问题答案: 我认为您找不到比问题中提出的3个总和更快的解决方案。numpy的优点对于较大的矢量以及在需要其他运算符时都是显而易见的。numpy对于矩阵特别有用,而python
问题内容: 我正在尝试在python中添加两个分数 如果输入1/4 + 1/4,我期望得到1/2结果 我用加法建立了一个分数类 但是我得到的输出是2,4,实际上是1/2,只是没有简化。我该如何解决这个问题? 问题答案: 简化分数的一般方法是找到分子和分母的最大公约数,然后将两者除以
问题内容: 我有两次弦乐时间 有没有简单的方法可以将这两个时间相加并获得一个新的时间 呢? 我想在客户端执行此操作,因此,如果可以避免使用任何日期数据库 问题答案: 请记住,您可以将小时/分钟/秒的整数值转换为单个整数,如下所示: 并转换回: 或者,您可以按以下步骤进行算术零碎:
到目前为止,我有以下代码: 当我运行这个脚本并向它传递一个参数时,我得到一个错误:“./adddir:line 3:[[1:command not found./adddir:line 3:[1:command not found” 当我使用2个参数而不是“1”运行它时,它会显示为“2” 有事吗?