相关疑难解决方法(0)

为什么在强度降低乘法和循环进位加法之后,这段代码的执行速度会变慢?

我正在阅读Agner Fog优化手册,并且遇到了这个例子:

double data[LEN];

void compute()
{
    const double A = 1.1, B = 2.2, C = 3.3;

    int i;
    for(i=0; i<LEN; i++) {
        data[i] = A*i*i + B*i + C;
    }
}
Run Code Online (Sandbox Code Playgroud)

Agner 指出,有一种方法可以优化此代码 - 通过认识到循环可以避免使用昂贵的乘法,而是使用每次迭代应用的“增量”。

我用一张纸来证实这个理论,首先......

理论

...当然,他是对的 - 在每次循环迭代中,我们可以通过添加“增量”,基于旧结果计算新结果。该增量从值“A+B”开始,然后每一步增加“2*A”。

所以我们将代码更新为如下所示:

void compute()
{
    const double A = 1.1, B = 2.2, C = 3.3;
    const double A2 = A+A;
    double Z = A+B;
    double Y = C;

    int i;
    for(i=0; i<LEN; i++) {
        data[i] …
Run Code Online (Sandbox Code Playgroud)

optimization assembly x86-64 simd cpu-architecture

320
推荐指数
6
解决办法
9万
查看次数

每个汇编指令需要多少个CPU周期?

我听说有英特尔在线书籍描述了特定汇编指令所需的CPU周期,但我无法找到它(经过努力).有人能告诉我如何找到CPU周期吗?

下面是一个例子,在下面的代码中,mov/lock是1个CPU周期,xchg是3个CPU周期.

// This part is Platform dependent!
#ifdef WIN32
inline int CPP_SpinLock::TestAndSet(int* pTargetAddress, 
                                              int nValue)
{
    __asm
    {
        mov edx, dword ptr [pTargetAddress]
        mov eax, nValue
        lock xchg eax, dword ptr [edx]
    }
    // mov = 1 CPU cycle
    // lock = 1 CPU cycle
    // xchg = 3 CPU cycles
}

#endif // WIN32
Run Code Online (Sandbox Code Playgroud)

顺便说一句:这是我发布的代码的URL:http://www.codeproject.com/KB/threads/spinlocks.aspx

cpu assembly cycle

48
推荐指数
5
解决办法
5万
查看次数

为什么mulss在Haswell上只用了3个周期,与Agner的指令表不同?

我是指令优化的新手.

我对一个简单的函数dotp进行了简单的分析,该函数用于获取两个浮点数组的点积.

C代码如下:

float dotp(               
    const float  x[],   
    const float  y[],     
    const short  n      
)
{
    short i;
    float suma;
    suma = 0.0f;

    for(i=0; i<n; i++) 
    {    
        suma += x[i] * y[i];
    } 
    return suma;
}
Run Code Online (Sandbox Code Playgroud)

我用昂纳雾在网络上提供的测试框架testp.

在这种情况下使用的数组是对齐的:

int n = 2048;
float* z2 = (float*)_mm_malloc(sizeof(float)*n, 64);
char *mem = (char*)_mm_malloc(1<<18,4096);
char *a = mem;
char *b = a+n*sizeof(float);
char *c = b+n*sizeof(float);

float *x = (float*)a;
float *y = (float*)b;
float *z = (float*)c;
Run Code Online (Sandbox Code Playgroud)

然后我调用函数dotp,n = 2048,repeat …

c optimization assembly sse micro-optimization

31
推荐指数
1
解决办法
1471
查看次数

了解lfence对具有两个长依赖链的循环的影响,以增加长度

我正在玩这个答案的代码,稍微修改一下:

BITS 64

GLOBAL _start

SECTION .text

_start:
 mov ecx, 1000000

.loop:

 ;T is a symbol defined with the CLI (-DT=...)

 TIMES T imul eax, eax
 lfence
 TIMES T imul edx, edx


 dec ecx
jnz .loop

 mov eax, 60           ;sys_exit
 xor edi, edi
 syscall
Run Code Online (Sandbox Code Playgroud)

没有lfence我,我得到的结果与答案中的静态分析一致.

当我介绍一个单一 lfence我期望的CPU执行imul edx, edx的序列的第k个平行于迭代imul eax, eax的下一个(的序列K + 1个)迭代.
像这样的东西(调用一个imul eax, eax序列和dimul edx, edx一个): …

performance x86 assembly cpu-architecture perf

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

确定数据流中的关键路径

Computer Systems:A Programmer's Perspective一书中,练习5.5显示了一段代码来计算多项式的值

double poly(double a[], double x, int degree)
{
    long int i;
    double result = a[0];
    double xpwr = x;
    for (i = 1; i <= degree; i++) {
        result += a[i] * xpwr;
        xpwr = x * xpwr;
    }
    return result;
}
Run Code Online (Sandbox Code Playgroud)

练习假设双精度浮点加法和乘法所需的时钟周期分别为3和5.要求读者解释为什么测量的CPE(每元素周期数)值为5.

按照习题答案,在每次迭代中,我们需要更新变量xpwrresult我们需要的,操作是一个浮点加法(对于result)和浮点乘法(对于xpwr),因此后者占主导地位的延迟,导致最终CPE为5.

但我认为数据流应该是这样的:

xpwr               result
  |                  |
  +-----+ +--[load]  |
  |     | |          |
[mul]  [mul]         |
  |      |           |
  |      +---+ +-----+ …
Run Code Online (Sandbox Code Playgroud)

cpu cpu-cycles computer-architecture

9
推荐指数
2
解决办法
4209
查看次数

在预测现代超标量处理器上的操作延迟时需要考虑哪些因素以及如何手动计算它们?

我希望能够手动预测任意算术的长度(即没有分支或内存,尽管这也很好)x86-64汇编代码将采用特定的体系结构,考虑到指令重新排序,超标量,延迟,消费者价格指数等

什么/描述必须遵循的规则才能实现这一目标?


我想我已经找到了一些初步规则,但是我没有找到任何关于将任何示例代码分解为这个详细程度的引用,所以我不得不做一些猜测.(例如,英特尔优化手册甚至几乎没有提到指令重新排序.)

至少,我正在寻找(1)确认每条规则是正确的,或者是每条规则的正确陈述,以及(2)我可能忘记的任何规则的列表.

  • 每个循环发出尽可能多的指令,从当前循环开始按顺序开始,并且可能与重新排序缓冲区大小一样远.
  • 如果出现以下情况,可以在给定周期发出指令:
    • 没有影响其操作数的指令仍在执行中.和:
    • 如果它是浮点指令,则它之前的每个浮点指令都被发出(浮点指令具有静态指令重新排序).和:
    • 该循环有一个功能单元可用于该指令.每个(?)功能单元是流水线的,这意味着它可以在每个周期接受1个新指令,并且对于给定功能类的CPI,总功能单元的数量是1/CPI(这里模糊不清:可能是例如addps并且subps使用相同的功能) unit?我如何确定?).和:
    • 4此循环已经发出少于超标量宽度(通常)指令的数量.
  • 如果不能发出指令,则处理器不会发出任何称为"停顿"的条件.

例如,请考虑以下示例代码(计算交叉产品):

shufps   xmm3, xmm2, 210
shufps   xmm0, xmm1, 201
shufps   xmm2, xmm2, 201
mulps    xmm0, xmm3
shufps   xmm1, xmm1, 210
mulps    xmm1, xmm2
subps    xmm0, xmm1
Run Code Online (Sandbox Code Playgroud)

我试图预测Haswell的延迟看起来像这样:

; `mulps`  Haswell latency=5, CPI=0.5
; `shufps` Haswell latency=1, CPI=1
; `subps`  Haswell latency=3, CPI=1

shufps   xmm3, xmm2, 210   ; cycle  1
shufps   xmm0, xmm1, 201   ; cycle  2
shufps   xmm2, xmm2, 201   ; …
Run Code Online (Sandbox Code Playgroud)

assembly pipeline latency x86-64 superscalar

8
推荐指数
1
解决办法
268
查看次数

指令减少 33%,内存访问减少 17%,但速度提高 4 倍?

概括

我有两段 C++ 代码,它们执行相同的计算。与代码 A 相比,代码 B 确实减少了大约 33% 的指令,大约减少了 17% 的内存访问,但运行速度是代码 A 的四倍(而不是两倍)。会是什么原因呢?此外,我们如何才能确认您的回答所提供的主张?

在这两个代码中,

  • howmany是 20 000 000
  • testees有 20 000 000 个元素,mt19937在启动时(在这些片段之前)为代码 A 和代码 B 随机生成 ( )。
  • 乘法是通过对内存的一次访问来处理的(如稍后在汇编代码中看到的)
  • 两个代码都是用优化标志编译的-O1

一些代码

代码 A - 运行时间约为。95 至 110 毫秒

    GF2 sum {GF2(1)};
    auto a = system_clock::now();
    for(size_t i=0;i<howmany;i++){
        sum *= testees[i]; 
    }
    auto b = system_clock::now();
Run Code Online (Sandbox Code Playgroud)

代码 B - 运行时间约为。25 至 30 毫秒

    GF2 sum1 {GF2(1)};
    GF2 sum2 {GF2(1)};
    GF2 sum3 …
Run Code Online (Sandbox Code Playgroud)

c++ performance assembly g++

5
推荐指数
0
解决办法
260
查看次数

优化累积总和

我需要一些帮助来了解我尝试的优化是如何工作的。

cumsum函数获取一个向量,并用累加和写入一个向量。

我尝试了以下方法来优化它:我没有在整个向量上执行一次循环,而是编写了一个循环,该循环在每四分之一的向量上同时运行。然后调整每个部分以考虑前面部分的总和。结果略有不同,但这不是问题。

这是程序:

module cumsum_mod
    implicit none
    integer, parameter, private :: dp = kind(1d0)
contains
    ! cumsum in one straight loop
    subroutine cumsum1(n, a, b)
        integer :: n, i
        real(dp) :: a(n), b(n)
        
        b(1) = a(1)
        do i = 2, n
            b(i) = a(i) + b(i-1)
        end do
    end subroutine
    
    subroutine cumsum2(n, a, b)
        integer :: n, i, m
        real(dp) :: a(n), b(n)
        
        m = n/4
        
        ! Loop over the four parts
        b(1) = a(1)
        b(1+m) = …
Run Code Online (Sandbox Code Playgroud)

optimization x86 assembly fortran x86-64

5
推荐指数
0
解决办法
81
查看次数

当源 = 目标、就地时,AVX512 自动向量化 C++ 矩阵向量函数要慢得多

我尝试编写一些函数来使用单个矩阵和源向量数组来执行矩阵向量乘法。我曾经用 C++ 编写过这些函数,并在 x86 AVX512 汇编中编写过一次,以将性能与英特尔 VTune Profiler 进行比较。当使用源向量数组作为目标数组时,汇编变体的执行速度比 C++ 对应版本快 3.5 倍到 10x\xc2\xa0,但是当使用不同的源和目标数组时,汇编变体的性能几乎不比 C++ 对应版本更好,实现几乎相同的性能...有时甚至更糟。

\n

我无法理解的另一件事是,为什么在使用不同的源和目标数组时,C++ 对应项甚至可以达到与汇编变体接近相同或更好的性能水平,即使汇编代码要短得多并且也根据静态分析工具 uica 和 llvm-mca 速度提高数倍。uica.uops.info

\n

我不想让这篇文章变得太长,所以我只发布执行 mat4-vec4 乘法的函数的代码。

\n

这是汇编变体的代码,它假设矩阵要转置:

\n
alignas(64) uint32_t mat4_mul_vec4_avx512_vpermps_index[64]{    0, 0, 0, 0, 4, 4, 4, 4, 8, 8, 8, 8, 12, 12, 12, 12,\n                                                            1, 1, 1, 1, 5, 5, 5, 5, 9, 9, 9, 9, 13, 13, 13, 13,\n                                                            2, 2, 2, 2, 6, 6, 6, 6, 10, 10, 10, 10, 14, 14, …
Run Code Online (Sandbox Code Playgroud)

c++ assembly x86-64 auto-vectorization avx512

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

英特尔内在函数中的延迟与吞吐量

我认为我对延迟和吞吐量之间的差异有一个很好的理解.但是,对于Intel Intrinsics来说,延迟对指令吞吐量的影响并不清楚,特别是在顺序(或几乎顺序)使用多个内部调用时.

例如,让我们考虑一下:

_mm_cmpestrc
Run Code Online (Sandbox Code Playgroud)

它的延迟为11,Haswell处理器的吞吐量为7.如果我在一个循环中运行这个指令,那么在11个循环后我会得到一个连续的每循环输出吗?由于这需要一次运行11条指令,并且因为我的吞吐量为7,所以我是否会用完"执行单元"?

我不确定如何使用延迟和吞吐量,除了得到一条指令相对于不同版本的代码需要多长时间的印象.

performance x86 sse intrinsics micro-optimization

3
推荐指数
1
解决办法
1419
查看次数

为什么 CPU 不能在一个简单的循环中实现相当于 Ghz 的 FLOP 性能?

我想知道为什么像这样的简单循环无法达到我的 CPU 时钟速度(4,2Ghz):

float sum = 0;    
for (int i = 0; i < 1000000; i+=1) {
    sum = sum * 1 + 1;
}
Run Code Online (Sandbox Code Playgroud)

凭直觉,我希望在不到 1 毫秒(例如 0,238 毫秒)的时间内实现这一目标,每秒进行 42 亿次迭代。但我得到的时间约为 3 毫秒,即每秒约 3.33 亿次迭代。

我假设做数学运算需要 2 个周期,一个用于乘法,另一个用于求和。假设我正在执行 6.66 亿次操作……看起来仍然很慢。然后我假设循环比较需要一个周期,循环计数器需要另一个周期......

所以我创建了以下代码来删除循环......

void listOfSums() {
    float internalSum = 0;
    internalSum = internalSum * 1 + 1;
    internalSum = internalSum * 1 + 1;
    internalSum = internalSum * 1 + 1;
    internalSum = internalSum * 1 + 1;
    // Repeated 100k …
Run Code Online (Sandbox Code Playgroud)

c cpu assembly

3
推荐指数
1
解决办法
312
查看次数