我一直试图找出应用程序中的性能问题,并最终将其缩小到一个非常奇怪的问题.如果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) 似乎是一个反复出现的问题,许多英特尔处理器(直到Skylake,除非我错了)在将AVX-256指令与SSE指令混合时表现不佳.
根据英特尔的文档,这是由SSE指令定义为保留YMM寄存器的高128位引起的,因此为了能够通过不使用AVX数据路径的高128位来节省功耗,CPU会存储这些位当执行SSE代码并在输入AVX代码时重新加载它们时,存储和加载是昂贵的.
但是,我找不到明显的理由或解释为什么SSE指令需要保留那些高128位.相应的128位VEX指令(使用它避免了性能损失)通过始终清除YMM寄存器的高128位而不是保留它们来工作.在我看来,当英特尔定义AVX架构,包括将XMM寄存器扩展到YMM寄存器时,他们可以简单地定义SSE指令也将清除高128位.显然,由于YMM寄存器是新的,可能没有遗留代码依赖于保留这些位的SSE指令,而且在我看来,英特尔可以很容易地看到这一点.
那么,英特尔定义SSE指令以保留YMM寄存器的高128位的原因是什么?它有用吗?
我发现这个职位,说明如何进行转一个8x8矩阵的字节24点的操作,和几个卷轴后有代码实现转置.但是,这种方法没有利用我们可以阻止 8x8转置为4个4x4转置的事实,并且每个转换只能在一个shuffle指令中完成(这篇文章是参考文献).所以我推出了这个解决方案:
__m128i transpose4x4mask = _mm_set_epi8(15, 11, 7, 3, 14, 10, 6, 2, 13, 9, 5, 1, 12, 8, 4, 0);
__m128i shuffle8x8Mask = _mm_setr_epi8(0, 1, 2, 3, 8, 9, 10, 11, 4, 5, 6, 7, 12, 13, 14, 15);
void TransposeBlock8x8(uint8_t *src, uint8_t *dst, int srcStride, int dstStride) {
__m128i load0 = _mm_set_epi64x(*(uint64_t*)(src + 1 * srcStride), *(uint64_t*)(src + 0 * srcStride));
__m128i load1 = _mm_set_epi64x(*(uint64_t*)(src + 3 * srcStride), *(uint64_t*)(src + …Run Code Online (Sandbox Code Playgroud) 我测试了以下简单的功能
void mul(double *a, double *b) {
for (int i = 0; i<N; i++) a[i] *= b[i];
}
Run Code Online (Sandbox Code Playgroud)
具有非常大的数组,因此它受内存带宽限制.我使用的测试代码如下.当我用-O2它编译它需要1.7秒.当我用-O2 -mavx它编译它只需要1.0秒.非vex编码的标量操作慢了70%!为什么是这样?
系统:i7-6700HQ@2.60GHz(Skylake)32 GB内存,Ubuntu 16.10,GCC 6.3
测试代码
//gcc -O2 -fopenmp test.c
//or
//gcc -O2 -mavx -fopenmp test.c
#include <string.h>
#include <stdio.h>
#include <x86intrin.h>
#include <omp.h>
#define N 1000000
#define R 1000
void mul(double *a, double *b) {
for (int i = 0; i<N; i++) a[i] …Run Code Online (Sandbox Code Playgroud)