相关疑难解决方法(0)

如果没有Skylake上的VZEROUPPER,为什么这个SSE代码会慢6倍?

我一直试图找出应用程序中的性能问题,并最终将其缩小到一个非常奇怪的问题.如果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)

performance x86 sse intel avx

32
推荐指数
2
解决办法
4072
查看次数

x86的MOV真的可以"免费"吗?为什么我不能重现这个呢?

我一直看到人们声称MOV指令可以在x86中免费,因为寄存器重命名.

对于我的生活,我无法在一个测试用例中验证这一点.每个测试用例我尝试揭穿它.

例如,这是我用Visual C++编译的代码:

#include <limits.h>
#include <stdio.h>
#include <time.h>

int main(void)
{
    unsigned int k, l, j;
    clock_t tstart = clock();
    for (k = 0, j = 0, l = 0; j < UINT_MAX; ++j)
    {
        ++k;
        k = j;     // <-- comment out this line to remove the MOV instruction
        l += j;
    }
    fprintf(stderr, "%d ms\n", (int)((clock() - tstart) * 1000 / CLOCKS_PER_SEC));
    fflush(stderr);
    return (int)(k + j + l);
}
Run Code Online (Sandbox Code Playgroud)

这为循环生成以下汇编代码(随意生成这个你想要的;你显然不需要Visual C++):

LOOP:
    add edi,esi
    mov …
Run Code Online (Sandbox Code Playgroud)

c x86 assembly cpu-registers micro-optimization

23
推荐指数
2
解决办法
2113
查看次数

使用AVX/AVX2转置8x8浮点数

通过制作四个4x4矩阵并转置每个矩阵,可以实现8x8矩阵的转置.这不是我想要的.

在另一个问题中,一个答案提供了一个解决方案,只需要24个8x8矩阵指令.但是,这不适用于花车.

由于AVX2包含256位寄存器,因此每个寄存器适合8个32位整数(浮点数).但问题是:

如何使用AVX/AVX2转换8x8浮点矩阵,尽可能使用最小的指令?

simd avx avx2

14
推荐指数
2
解决办法
6323
查看次数

如何使用SIMD指令转置16x16矩阵?

我目前正在编写一些针对英特尔即将推出的AVX-512 SIMD指令的代码,该指令支持512位操作.

现在假设有一个由16个SIMD寄存器表示的矩阵,每个寄存器包含16个32位整数(对应一行),如何用纯SIMD指令转置矩阵?

已经有解决方案分别用SSE和AVX2转置4x4或8x8矩阵.但我无法弄清楚如何使用AVX-512将其扩展到16x16.

有任何想法吗?

assembly intel simd matrix avx512

13
推荐指数
2
解决办法
4722
查看次数

使用openmp parallel for loop意外地表现出色

我之前的评论(特别是@Zboson)之后我编辑了我的问题,以提高可读性

我一直采取行动并观察传统观点,即openmp线程的数量应与机器上的超线程数大致匹配,以获得最佳性能.但是,我观察到我的新笔记本电脑采用Intel Core i7 4960HQ,4核 - 8线程的奇怪行为.(请参阅此处的英特尔文档)

这是我的测试代码:

#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <omp.h>

int main() {
    const int n = 256*8192*100;
    double *A, *B;
    posix_memalign((void**)&A, 64, n*sizeof(double));
    posix_memalign((void**)&B, 64, n*sizeof(double));
    for (int i = 0; i < n; ++i) {
        A[i] = 0.1;
        B[i] = 0.0;
    }
    double start = omp_get_wtime();
    #pragma omp parallel for
    for (int i = 0; i < n; ++i) {
        B[i] = exp(A[i]) + sin(B[i]);
    }
    double end = omp_get_wtime(); …
Run Code Online (Sandbox Code Playgroud)

parallel-processing multithreading gcc openmp avx

12
推荐指数
1
解决办法
2241
查看次数

为什么SSE指令保留YMM寄存器的高128位?

似乎是一个反复出现的问题,许多英特尔处理器(直到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位的原因是什么?它有用吗?

performance x86 avx

12
推荐指数
1
解决办法
551
查看次数

转换4x4字节矩阵的最快方法

我有一个4x4字节块,我想使用通用硬件进行转置.换句话说,对于字节AP,我正在寻找最有效(就指令数量而言)的方式

A B C D
E F G H
I J K L
M N O P
Run Code Online (Sandbox Code Playgroud)

A E I M
B F J N
C G K O
D H L P
Run Code Online (Sandbox Code Playgroud)

我们可以假设,我有有效指针指向A,E,I,并M在内存中(这样从读取32位将让我的包含整数字节ABCD).

由于对大小和数据类型的限制,这不是此问题的重复.我的矩阵的每一行都可以适合32位整数,我正在寻找可以使用通用硬件快速执行转置的答案,类似于SSE宏的实现_MM_TRANSPOSE4_PS.

c c++ optimization bit-manipulation matrix

7
推荐指数
2
解决办法
3634
查看次数

在SSE2/SSSE3上转换8个16位元素寄存器

(我是SSE/asm的新手,如果这显而易见或多余则道歉)

是否有更好的方法来转换包含16位值的8个SSE寄存器,而不是执行24个unpck [lh] ps和8/16 + shuffle以及使用8个额外的寄存器?(注意最多使用SSSE 3指令,Intel Merom,又称SSE4缺少BLEND*.)

假设你有寄存器v [0-7]并使用t0-t7作为辅助寄存器.在伪内在函数代码中:

/* Phase 1: process lower parts of the registers */
/* Level 1: work first part of the vectors */
/*   v[0]  A0 A1 A2 A3 A4 A5 A6 A7
**   v[1]  B0 B1 B2 B3 B4 B5 B6 B7
**   v[2]  C0 C1 C2 C3 C4 C5 C6 C7
**   v[3]  D0 D1 D2 D3 D4 D5 D6 D7
**   v[4]  E0 E1 E2 E3 E4 E5 E6 E7 …
Run Code Online (Sandbox Code Playgroud)

x86 assembly sse simd matrix

6
推荐指数
2
解决办法
3310
查看次数

在标量矩阵加法中使用 vaddss 代替 adds 有什么好处?

我已经实现了标量矩阵加法内核。

#include <stdio.h>
#include <time.h>
//#include <x86intrin.h>

//loops and iterations:
#define N 128
#define M N
#define NUM_LOOP 1000000


float   __attribute__(( aligned(32))) A[N][M],
        __attribute__(( aligned(32))) B[N][M],
        __attribute__(( aligned(32))) C[N][M];

int main()
{
int w=0, i, j;
struct timespec tStart, tEnd;//used to record the processiing time
double tTotal , tBest=10000;//minimum of toltal time will asign to the best time
do{
    clock_gettime(CLOCK_MONOTONIC,&tStart);

    for( i=0;i<N;i++){
        for(j=0;j<M;j++){
            C[i][j]= A[i][j] + B[i][j];
        }
    }

    clock_gettime(CLOCK_MONOTONIC,&tEnd);
    tTotal = (tEnd.tv_sec - tStart.tv_sec);
    tTotal += (tEnd.tv_nsec - tStart.tv_nsec) …
Run Code Online (Sandbox Code Playgroud)

x86 assembly gcc sse avx

5
推荐指数
1
解决办法
1971
查看次数

AVX标量操作要快得多

我测试了以下简单的功能

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%!为什么是这样?

这里是为大会-O2-O2 -mavx. <code> -O2 </ code>和<code> -O2 -mavx </ code>的vimddif

https://godbolt.org/g/w4p60f

系统: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)

c memory x86 sse avx

5
推荐指数
1
解决办法
490
查看次数

我正在尝试使用4向循环展开来优化此c代码

我正在尝试做的是使用这个C代码并使用一种称为循环展开的技术对其进行优化,但在这种情况下,我想使用四向循环展开.现在,我理解了这项技术,并且我理解了我不知道如何将其应用于此代码的概念.我是否需要添加一些额外的变量?在每个循环之后或者只是在所有循环结束时,我是否必须有一些代码?此代码是8x8块代码,用于处理像素并逆时针旋转90度.任何帮助将不胜感激.谢谢.

/* 
 * rotate8 - rotate with 8x8 blocking
 */

char rotate8_descr[] = "rotate8: rotate with 8x8 blocking";

void rotate8(int dim, pixel *src, pixel *dst) 
{

int i, j, ii, jj;

for(ii = 0; ii < dim; ii += 8)
       for(jj = 0; jj < dim; jj += 8)
              for (i = ii; i < ii + 8; i++)   
                  for (j = jj; j < jj + 8; j++)
                      dst[RIDX(dim-1-j, i, dim)] = src[RIDX(i, j, dim)];
}
Run Code Online (Sandbox Code Playgroud)

c

2
推荐指数
1
解决办法
3568
查看次数