我一直试图找出应用程序中的性能问题,并最终将其缩小到一个非常奇怪的问题.如果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) 我试图通过SSE和AVX提高复制操作的性能:
#include <immintrin.h>
const int sz = 1024;
float *mas = (float *)_mm_malloc(sz*sizeof(float), 16);
float *tar = (float *)_mm_malloc(sz*sizeof(float), 16);
float a=0;
std::generate(mas, mas+sz, [&](){return ++a;});
const int nn = 1000;//Number of iteration in tester loops
std::chrono::time_point<std::chrono::system_clock> start1, end1, start2, end2, start3, end3;
//std::copy testing
start1 = std::chrono::system_clock::now();
for(int i=0; i<nn; ++i)
std::copy(mas, mas+sz, tar);
end1 = std::chrono::system_clock::now();
float elapsed1 = std::chrono::duration_cast<std::chrono::microseconds>(end1-start1).count();
//SSE-copy testing
start2 = std::chrono::system_clock::now();
for(int i=0; i<nn; ++i)
{
auto _mas = mas;
auto _tar = tar; …Run Code Online (Sandbox Code Playgroud) 我理解VZEROUPPER在混合SSE和AVX代码时使用很重要,但如果我只使用AVX(和普通的x86-64代码)而不使用任何传统的SSE指令呢?
如果我从不在我的代码中使用单个SSE指令,是否有任何性能原因我需要使用VZEROUPPER?
这假设我没有调用任何外部库(可能使用SSE).
编写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)