- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我想向量化两个内存对齐数组的乘法。
我没有找到在AVX / AVX2中乘以64 * 64位的任何方法,所以我只是循环展开和AVX2加载/存储。有更快的方法吗?
注意:我不想保存每个乘法的上半部分结果。
void multiply_vex(long *Gi_vec, long q, long *Gj_vec){
int i;
__m256i data_j, data_i;
__uint64_t *ptr_J = (__uint64_t*)&data_j;
__uint64_t *ptr_I = (__uint64_t*)&data_i;
for (i=0; i<BASE_VEX_STOP; i+=4) {
data_i = _mm256_load_si256((__m256i*)&Gi_vec[i]);
data_j = _mm256_load_si256((__m256i*)&Gj_vec[i]);
ptr_I[0] -= ptr_J[0] * q;
ptr_I[1] -= ptr_J[1] * q;
ptr_I[2] -= ptr_J[2] * q;
ptr_I[3] -= ptr_J[3] * q;
_mm256_store_si256((__m256i*)&Gi_vec[i], data_i);
}
for (; i<BASE_DIMENSION; i++)
Gi_vec[i] -= Gj_vec[i] * q;
}
-=
替换了
_mm256_sub_epi64
,在此可以加快速度。目前是
ptr_J[0] *= q; ...
__uint64_t
,但是是
错误。正确的数据类型是
__int64_t
。
最佳答案
您似乎假设long
在您的代码中为64位,但随后也使用__uint64_t
。在32bit中,x32 ABI,在Windows中,long
是32bit类型。您的标题中提到long long
,但是您的代码将其忽略。我想知道您的代码是否假设long
是32位。
通过使用AVX256负载,您可以完全射击自己的脚,然后将指针混叠到__m256i
上以执行标量操作。 gcc只是放弃了,给了您所要求的糟糕代码: vector 加载,然后是一堆extract
和insert
指令。您的编写方式意味着还必须解压缩和两个 vector ,以便也可以按标量执行sub
,而不是使用vpsubq
。
Modern x86 CPUs have very fast L1 cache that can handle two operations per clock。 (Haswell和更高版本:每个时钟两个负载和一个存储)。从同一缓存行执行多个标量加载比 vector 加载和拆包更好。 (但是,不完善的uop调度将吞吐量降低到了其中的84%,请参见下文)
gcc 5.3 -O3 -march=haswell (Godbolt compiler explorer)很好地自动矢量化了一个简单的标量实现。 当没有AVX2时,gcc仍然会愚蠢地使用128b vector 自动矢量化:在Haswell上,这实际上是理想标量64位代码速度的1/2。 (请参阅下面的性能分析,但每个 vector 用2个元素代替4个)。
#include <stdint.h> // why not use this like a normal person?
#define BASE_VEX_STOP 1024
#define BASE_DIMENSION 1028
// restrict lets the compiler know the arrays don't overlap,
// so it doesn't have to generate a scalar fallback case
void multiply_simple(uint64_t *restrict Gi_vec, uint64_t q, const uint64_t *restrict Gj_vec){
for (intptr_t i=0; i<BASE_DIMENSION; i++) // gcc doesn't manage to optimize away the sign-extension from 32bit to pointer-size in the scalar epilogue to handle the last less-than-a-vector elements
Gi_vec[i] -= Gj_vec[i] * q;
}
.L4:
vmovdqu ymm1, YMMWORD PTR [r9+rax] # MEM[base: vectp_Gj_vec.22_86, index: ivtmp.32_76, offset: 0B], MEM[base: vectp_Gj_vec.22_86, index: ivtmp.32_76, offset: 0B]
add rcx, 1 # ivtmp.30,
vpsrlq ymm0, ymm1, 32 # tmp174, MEM[base: vectp_Gj_vec.22_86, index: ivtmp.32_76, offset: 0B],
vpmuludq ymm2, ymm1, ymm3 # tmp173, MEM[base: vectp_Gj_vec.22_86, index: ivtmp.32_76, offset: 0B], vect_cst_.25
vpmuludq ymm0, ymm0, ymm3 # tmp176, tmp174, vect_cst_.25
vpmuludq ymm1, ymm4, ymm1 # tmp177, tmp185, MEM[base: vectp_Gj_vec.22_86, index: ivtmp.32_76, offset: 0B]
vpaddq ymm0, ymm0, ymm1 # tmp176, tmp176, tmp177
vmovdqa ymm1, YMMWORD PTR [r8+rax] # MEM[base: vectp_Gi_vec.19_81, index: ivtmp.32_76, offset: 0B], MEM[base: vectp_Gi_vec.19_81, index: ivtmp.32_76, offset: 0B]
vpsllq ymm0, ymm0, 32 # tmp176, tmp176,
vpaddq ymm0, ymm2, ymm0 # vect__13.24, tmp173, tmp176
vpsubq ymm0, ymm1, ymm0 # vect__14.26, MEM[base: vectp_Gi_vec.19_81, index: ivtmp.32_76, offset: 0B], vect__13.24
vmovdqa YMMWORD PTR [r8+rax], ymm0 # MEM[base: vectp_Gi_vec.19_81, index: ivtmp.32_76, offset: 0B], vect__14.26
add rax, 32 # ivtmp.32,
cmp rcx, r10 # ivtmp.30, bnd.14
jb .L4 #,
-O3
进行编译,则可以在循环之前使用
#pragma omp simd
(和
-fopenmp
)。
set1(q)
传递为
b
,而不是
a
,以便可以悬挂“bswap”洗牌。
// replace hadd -> shuffle (4 uops) with shift/add/and (3 uops)
// The constant takes 2 insns to generate outside a loop.
__m256i mul64_haswell (__m256i a, __m256i b) {
// instruction does not exist. Split into 32-bit multiplies
__m256i bswap = _mm256_shuffle_epi32(b,0xB1); // swap H<->L
__m256i prodlh = _mm256_mullo_epi32(a,bswap); // 32 bit L*H products
// or use pshufb instead of psrlq to reduce port0 pressure on Haswell
__m256i prodlh2 = _mm256_srli_epi64(prodlh, 32); // 0 , a0Hb0L, 0, a1Hb1L
__m256i prodlh3 = _mm256_add_epi32(prodlh2, prodlh); // xxx, a0Lb0H+a0Hb0L, xxx, a1Lb1H+a1Hb1L
__m256i prodlh4 = _mm256_and_si256(prodlh3, _mm256_set1_epi64x(0x00000000FFFFFFFF)); // zero high halves
__m256i prodll = _mm256_mul_epu32(a,b); // a0Lb0L,a1Lb1L, 64 bit unsigned products
__m256i prod = _mm256_add_epi64(prodll,prodlh4); // a0Lb0L+(a0Lb0H+a0Hb0L)<<32, a1Lb1L+(a1Lb1H+a1Hb1L)<<32
return prod;
}
imul r64, m64
在Intel CPU上的每个时钟的吞吐量为1(在AMD Bulldozer系列中为每4时钟的吞吐量)。 load / imul / sub-with-memory-dest是Intel CPU上的4个融合域uops(具有可微熔的寻址模式,gcc无法使用)。流水线宽度是每个时钟4个融合域uops,因此即使是大的展开也无法在每个时钟一个时钟上发布。如果展开得足够多,我们将成为加载/存储吞吐量的瓶颈。在Haswell上,每个时钟可以进行2次加载和一个存储,但是窃取加载端口的store-address uops将为
lower the throughput to about 81/96 = 84% of that, according to Intel's manual。
vmovq
/
pinsrq
/
vinserti128
,以便可以使用
vpsubq
进行减法。这是8个uops来加载&乘以所有4个标量,这是7个shuffle uops来将数据放入__m256i(2(movq)+ 4(pinsrq是2 uops)+ 1 vinserti128),还有3个uops进行 vector 加载/ vpsubq / vector商店。因此,每4乘以18个融合域uops(发出4.5个周期),但是有7个shuffle uops(执行7个周期)。因此,nvm与纯标量相比并不好。
pshufb
(端口5)代替移位,以移动数据并以零移位。 (其他随机播放不支持清零,而不是从输入中复制字节,并且输入中没有任何我们可以复制的已知零。)
paddq
/
psubq
可以在Haswell的端口1/5或Skylake的p015上运行。
pmuludq
和立即数 vector 移位,因此理论上它可以管理每个max(5/2,8/3,11/4)= 11/4 = 2.75周期一个 vector 的吞吐量。因此,它限制了融合域uop的总吞吐量(包括2个 vector 负载和1个 vector 存储)。因此,展开一些循环会有所帮助。由于调度不完善而造成的资源冲突可能会将其瓶颈限制在每个时钟少于4个融合域的范围。希望该循环开销可以在端口6上运行,该端口只能处理某些标量运算,包括
add
和compare-and-branch,而将端口0/1/5留给 vector ALU op,因为它们接近饱和(8/3 = 2.666个时钟)。不过,加载/存储端口远没有达到饱和。
dst + (src-dst)
和递增
dst
)。
-mavx512dq
,gcc可以使用它自动矢量化。
.L4:
vmovdqu64 zmm0, ZMMWORD PTR [r8+rax] # vect__11.23, MEM[base: vectp_Gj_vec.22_86, index: ivtmp.32_76, offset: 0B]
add rcx, 1 # ivtmp.30,
vpmullq zmm1, zmm0, zmm2 # vect__13.24, vect__11.23, vect_cst_.25
vmovdqa64 zmm0, ZMMWORD PTR [r9+rax] # MEM[base: vectp_Gi_vec.19_81, index: ivtmp.32_76, offset: 0B], MEM[base: vectp_Gi_vec.19_81, index: ivtmp.32_76, offset: 0B]
vpsubq zmm0, zmm0, zmm1 # vect__14.26, MEM[base: vectp_Gi_vec.19_81, index: ivtmp.32_76, offset: 0B], vect__13.24
vmovdqa64 ZMMWORD PTR [r9+rax], zmm0 # MEM[base: vectp_Gi_vec.19_81, index: ivtmp.32_76, offset: 0B], vect__14.26
add rax, 64 # ivtmp.32,
cmp rcx, r10 # ivtmp.30, bnd.14
jb .L4 #,
vpmullq
的速度比您可以用32位块构建的任何东西都要快得多,因此即使当前的CPU没有64位元素 vector 乘法硬件,也非常有必要为此提供一条指令。 (大概他们使用FMA单位的尾数乘法器。)
关于c - 乘以int64_t数组的最快方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37296289/
根据在线文档,这些固定宽度整数类型之间存在差异。对于 int*_t,我们将宽度固定为 * 的值。然而对于其他两种类型,描述中使用形容词最快和最小来请求底层数据模型提供的最快或最小实例。 “最快”或“最
我是 Python 注释的新手(类型提示)。我注意到 pyi 中的许多类定义文件继承到 Generic[_T] , 和 _T = TypeVar('_T') . 我很困惑,_T 是什么意思?这里的意思
这个问题在这里已经有了答案: How to use a variable inside a _T wrapper? (3 个答案) 关闭 7 年前。 我有以下代码: CString port = m
要包含 _T() 宏,我应该包含什么文件?它转换我认为的文本文字。我以为它是 windows.h,但我已经包含了它。 令人惊讶的是,我无法在 Google 上找到答案。 最佳答案 我在主题 Unico
类型的后缀 _t 由 POSIX 保留,但是如果我在自己的命名空间中使用 _t 后缀定义自己的类型怎么办? 最佳答案 我同意 user6366161 的 answer,其中说“C 对 namespac
我知道 size_t 有 _t 后缀,因为它的别名/typedef。但是我不明白为什么 char16_t, char32_t 和 wchar_t 包含 _t 后缀。 最佳答案 对于 wchar_t :
我想让这个字符串的主机名部分可变..目前,它只修复了这个 URL: _T(" --url=http://www.myurl.com/ --out=c:\\current.png"); 我想做这样的东西
这个问题在这里已经有了答案: convert string to _T in cpp (6 个答案) 关闭 7 年前。 string pagexx = "http://website.com/" +
我有一个注册为 COM 对象的 .net 库,当在 C++ 项目中导入 .tlb 文件时,我得到这样的方法声明 virtual HRESULT __stdcall GetBid ( /*[
我现在遇到了很多 Unicode 问题。据我了解,TCHAR 被定义为 wchar_t 或 char,具体取决于 _UNICODE 是否在某处定义,并且还有各种其他功能可以帮助解决这个问题。显然 _T
关闭。这个问题需要details or clarity .它目前不接受答案。 想改进这个问题吗? 通过 editing this post 添加细节并澄清问题. 关闭 9 年前。 Improve t
我正在尝试使用 _t() 方法翻译一个 DataObject。 我一直在 Pages 上使用它没有问题,但它似乎不适用于数据对象。 class SliderItem extends DataObjec
关于保留 _t 结尾名称的规则是否也适用于作用域名称(例如,在 namespace 或类中定义的类型和类型定义),还是仅适用于全局 namespace 中的类型和类型定义?标准 C/C++ 库或 PO
我确定以前有人问过这个问题,但我无法搜索到文本。如果有人可以解释它们,请给我推荐一篇文章,或者给我正确的搜索查询,我将不胜感激。 谢谢。 最佳答案 这只是一种预感,但看看 Wikipedia C++1
我有一个 UNICODE 应用程序,我们使用 _T(x) 定义如下。 #if defined(_UNICODE) #define _T(x) L ##x #else #define _T(x) x #
我一直想知道是否存在任何命名约定,例如何时对类型使用 ALLCAPS 以及何时附加 _t(以及何时不使用任何东西?)。我知道以前 K&R 发布了各种关于如何使用 C 的文档,但我找不到任何相关内容。
这似乎是一个简单的问题,但我无法通过 Stack Overflow 搜索或 Google 找到它。类型后跟 _t 是什么意思?比如 int_t anInt; 我在 C 代码中经常看到它与硬件密切相关—
C++ 有时使用后缀 _type关于类型定义(例如 std::vector::value_type ),有时_t (例如 std::size_t ),或者没有后缀(普通类,还有像 std::strin
字符串中的“T”代表什么。例如 _T("Hello")。我在需要 unicode 支持的项目中看到了这一点。它实际上告诉处理器什么 最佳答案 _T 代表“文本”。当且仅当您使用 Unicode 支持编
我的代码可以根据 C++ 类型识别您需要使用的 GL 类型。我想制作它的 _t 版本(如 std::decay_t 或 std::enable_if_t)但公开 int常量值 template st
我是一名优秀的程序员,十分优秀!