我一直试图找出应用程序中的性能问题,并最终将其缩小到一个非常奇怪的问题.如果VZEROUPPER指令被注释掉,则下面的代码在Skylake CPU(i5-6500)上运行速度慢6倍.我测试了Sandy Bridge和Ivy Bridge CPU,两种版本都以相同的速度运行,有或没有VZEROUPPER.
现在我VZEROUPPER对这个代码有了一个相当好的想法,而且我认为当没有VEX编码指令并且没有调用可能包含它们的任何函数时,它对这个代码根本不重要.事实上它不支持其他支持AVX的CPU似乎支持这一点.英特尔®64和IA-32架构优化参考手册中的表11-2也是如此
那么发生了什么?
我留下的唯一理论是,CPU中存在一个错误,它错误地触发了"保存AVX寄存器的上半部分"程序,而不应该这样做.或者其他一些同样奇怪的东西.
这是main.cpp:
#include <immintrin.h>
int slow_function( double i_a, double i_b, double i_c );
int main()
{
/* DAZ and FTZ, does not change anything here. */
_mm_setcsr( _mm_getcsr() | 0x8040 );
/* This instruction fixes performance. */
__asm__ __volatile__ ( "vzeroupper" : : : );
int r = 0;
for( unsigned j = 0; j < 100000000; ++j )
{
r |= slow_function(
0.84445079384884236262,
-6.1000481519580951328, …Run Code Online (Sandbox Code Playgroud) 我想我应该熟悉x86 SIMD扩展.但在我开始之前,我遇到了麻烦.我无法找到关于哪些仍然相关的良好概述.
几十年来,x86架构积累了大量的数学/多媒体扩展:
较新的超集是旧的超集,反之亦然?或者它们是互补的吗?
有些人已被弃用吗?哪些仍然相关?我听说过"遗留SSE".
有些是互斥的吗?即他们共享相同的硬件部分?
我应该一起使用哪个来最大化现代Intel/AMD CPU的硬件利用率?为了争论,让我们假设我可以找到适当的指令用途...如果没有别的话,用CPU加热我的房子.
编写ZMM寄存器可以使Skylake-X(或类似的)CPU无限期地处于最大涡流降低的状态。(SIMD指令可降低CPU频率并动态确定恶意AVX-512指令在何处执行)推测Ice Lake是类似的。
(解决方法:zmm16..31不是问题,据@ BeeOnRope的意见,我在报?是有用的,如果你的程序+库不包含SSE指令使用VZEROUPPER
所以这strlen的可以只使用vpxord xmm16,xmm16,xmm16和vpcmpeqb,与zmm16)
@BeeOnRope发布测试代码在RWT线:更换vbroadcastsd zmm15, [zero_dp]同vpcmpeqb k0, zmm0, [rdi]为“弄脏”指令,看看是否能运行后循环慢或快。
我假设执行任何512位uop都会暂时触发减少的turbo(同时关闭向量ALU uops的端口1,而512位uop实际上在后端),但问题是:CPU能否在其上恢复如果您vzeroupper仅在读取 ZMM寄存器后就从未使用过,您是否拥有?
(和/或以后的SSE或AVX指令是否会有过渡惩罚或错误的依赖关系?)
具体来说,这样的strlen使用insns vzeroupper在返回之前是否需要a ? (实际上,在任何实际的CPU上,和/或Intel记录的有关面向未来的最佳实践。)假定以后的指令可能包括非VEX SSE和/或VEX编码的AVX1 / 2,而不仅仅是GP整数,以防万一。这与使turbo减少的上256脏情况有关。
; check 64 bytes for zero, strlen building block.
vpxor xmm0,xmm0,xmm0 ; zmm0 = 0 using AVX1 implicit zero-extension
vpcmpeqb k0, zmm0, [rdi] ; 512-bit load + ALU, not micro-fused
;kortestq k0,k0 / …Run Code Online (Sandbox Code Playgroud) 我们的64位应用程序有很多代码(特别是在标准库中),它们在SSE模式下使用xmm0-xmm7寄存器.
我想使用ymm寄存器实现快速内存复制.我无法修改使用xmm寄存器添加VEX前缀的所有代码,我也认为这是不实际的,因为它会增加代码的大小,因为需要CPU解码更大的指令会使它运行得更慢.
我只是想使用两个ymm寄存器(可能是zmm - 支持zmm的经济型处理器可以在今年推出)用于快速内存复制.
问题是:如何使用ymm寄存器但避免过渡处罚?
当我使用ymm8-ymm15寄存器(不是ymm0-ymm7)时会发生惩罚吗?SSE最初有8个128位寄存器(xmm0-xmm7),但在64位模式下,(xmm8-xmm15)也可用于非VEX前缀指令.但是,我已经审查了我们的64位应用程序,它只使用xmm0-xmm7,因为它也有一个32位版本,几乎相同的代码.仅当CPU尝试使用之前使用过的xmm寄存器为ymm并且具有高128位非零值时才会发生惩罚吗?将快速内存复制后使用的ymm寄存器归零是不是更好?例如,我曾使用ymm寄存器复制32个字节的内存 - 将它归零的最快方法是什么?"vpxor ymm15,ymm15,ymm15"足够快吗?(AFAIK,vpxor可以在3个ALU执行端口中的任何一个上执行,p0/p1/p5,而vxorpd只能在p5上执行).是不是将它归零的时间超过使用它来复制32字节内存的收益?
最初,我试图重现 Agner Fog 的微架构指南部分“YMM 和 ZMM 向量指令的预热期”中描述的效果,其中写道:
\n\n\n处理器在不使用时关闭向量执行单元的上部部分,以节省电力。在大约 56,000 个时钟周期或 14 \xce\xbcs 的初始预热期间,具有 256 位向量的指令的吞吐量大约比正常情况慢 4.5 倍。
\n
我得到了减速,尽管看起来更接近 2 倍而不是 4.5 倍。但我发现在我的 CPU(Intel i7-9750H Coffee Lake)上,速度下降不仅影响 256 位操作,还影响 128 位向量操作和标量浮点操作(甚至 N 个 GPR-仅限 XMM 触摸指令之后的指令)。
\n基准程序代码:
\n# Compile and run:\n# clang++ ymm-throttle.S && ./a.out\n\n.intel_syntax noprefix\n\n.data\nL_F0:\n .asciz "ref cycles = %u\\n"\n\n.p2align 5\nL_C0:\n .long 1\n .long 2\n .long 3\n .long 4\n .long 1\n .long 2\n .long 3\n .long 4\n\n.text\n\n.set initial_scalar_warmup, 5*1000*1000\n.set iteration_count, 30*1000\n.set wait_count, 50*1000\n\n.global _main\n_main:\n …Run Code Online (Sandbox Code Playgroud) 我想知道是否可以按照以下方式做一些事情:
vpcmpeqb %ymm16, %ymm17, %ymm16
Run Code Online (Sandbox Code Playgroud)
尝试使用 gcc 进行编译,我得到:
Assembler messages: Error: unsupported instruction `vpcmpeqb'
Run Code Online (Sandbox Code Playgroud)
AFAICT 这是不可能的felixcloutier说唯一的 EVEX 前缀指令cmpeq有一个掩码目的地,但可能我遗漏了一些东西,或者直接用字节编码来做到这一点。
谢谢!
假设以AVX2为目标的编译和C++内在函数,如果我每个体体计算使用17个寄存器编写一个nbody算法,那么第17个寄存器可以间接(寄存器重命名硬件)或直接(visual studio编译器,gcc编译器)映射到AVX上-512注册以减少内存依赖关闭?例如,skylake架构有1或2个AVX-512 fma单元.这个数字是否也改变了总寄存器?(具体来说,至强银4114 cpu)
如果这样可行,它是如何工作的?当所有指令都是AVX2或更低时,第一个硬件线程使用每个ZMM向量的前半部分和第二个硬件线程使用每个ZMM向量的后半部分?
编辑:如果在目标机器上进行在线编译会怎样(例如,使用OpenCL)?司机可以为我注册使用吗?
x86 cpu-architecture cpu-registers compiler-optimization avx512
我最近被介绍了向量指令(理论上)并且对如何使用它们来加速我的应用程序感到兴奋。
我想改进的一个方面是一个非常热的循环:
__declspec(noinline) void pleaseVectorize(int* arr, int* someGlobalArray, int* output)
{
for (int i = 0; i < 16; ++i)
{
auto someIndex = arr[i];
output[i] = someGlobalArray[someIndex];
}
for (int i = 0; i < 16; ++i)
{
if (output[i] == 1)
{
return i;
}
}
return -1;
}
Run Code Online (Sandbox Code Playgroud)
但是,当然,所有 3 个主要编译器(msvc、gcc、clang)都拒绝对此进行矢量化。我可以理解为什么,但我想得到确认。
如果我必须手动矢量化它,它将是:
(1) VectorLoad "arr", 这带来了 16 个 4 字节整数,让我们说到 zmm0
(2) 16个内存从zmm0[0..3]指向的地址加载到zmm1[0..3],从zmm0[4..7]指向的地址加载到zmm1[4..7]所以等等
(3)比较zmm0和zmm1
(4) 向量 popcnt 到输出中找出最高有效位并基本上除以 8 得到匹配的索引
首先,向量指令可以做这些事情吗?就像他们可以执行这种“收集”操作,即从指向 zmm0 的地址加载?
以下是 clang 生成的内容:
0000000000400530 …Run Code Online (Sandbox Code Playgroud) 我在 corei7 上的 ubuntu 上运行 gcc 版本 4.8.2。
从谷歌搜索中找到了有关 AVX 内在函数的信息,但我不确定这组内在函数是否可以用于 Linux 设备驱动程序并对其进行编译。
如果可以,这里的任何人都可以告诉我什么是 makefile 的正确设置以及在 c 源中包含哪些头文件以使用 gcc 编译这个 avx?
谢谢。
在某些 Intel 处理器上混合使用和不使用 AVX 支持编译的代码时会出现问题。由于 YMM 寄存器状态的变化,从 AVX 代码到非 AVX 代码会导致性能下降。在从 AVX 代码到非 AVX 代码的任何转换之前,应该通过调用内部函数 _mm256_zeroupper() 来避免这种惩罚。在以下情况下,这可能是必要的:
• 如果程序的一部分是使用 AVX 支持编译的,而程序的另一部分是在没有 AVX 支持的情况下编译的,则在离开 AVX 部分之前调用 _mm256_zeroupper()。
• 如果使用 CPU 调度在使用和不使用 AVX 的多个版本中编译函数,则在离开 AVX 部分之前调用 _mm256_zeroupper()。
• 如果一段使用AVX 支持编译的代码调用了编译器自带的库以外的库中的函数,而该库没有AVX 支持,则在调用库函数之前先调用_mm256_zeroupper()。
我想知道什么是英特尔处理器。具体来说,是否有过去五年制造的处理器。这样我就知道修复丢失的_mm256_zeroupper()电话是否为时已晚。
avx ×7
x86 ×7
avx512 ×6
assembly ×5
sse ×5
intel ×4
simd ×4
c++ ×2
x86-64 ×2
avx2 ×1
gcc ×1
intrinsics ×1
linux-kernel ×1
mmx ×1
optimization ×1
performance ×1