相关疑难解决方法(0)

为什么循环总是被编译成"do ... while"样式(尾部跳转)?

当试图理解汇编(启用编译器优化)时,我看到这种行为:

这样一个非常基本的循环

outside_loop;
while (condition) {
     statements;
}
Run Code Online (Sandbox Code Playgroud)

经常被编译成(伪代码)

    ; outside_loop
    jmp loop_condition    ; unconditional
loop_start:
    loop_statements
loop_condition:
    condition_check
    jmp_if_true loop_start
    ; outside_loop
Run Code Online (Sandbox Code Playgroud)

但是,如果未打开优化,则会编译为通常可理解的代码:

loop_condition:
    condition_check
    jmp_if_false loop_end
    loop_statements
    jmp loop_condition  ; unconditional
loop_end:
Run Code Online (Sandbox Code Playgroud)

根据我的理解,编译后的代码更像是这样的:

goto condition;
do {
    statements;
    condition:
}
while (condition_check);
Run Code Online (Sandbox Code Playgroud)

我看不到巨大的性能提升或代码可读性提升,为什么经常出现这种情况呢?是否有此循环样式的名称,例如"尾随条件检查"?

optimization performance assembly loops micro-optimization

26
推荐指数
1
解决办法
1675
查看次数

在执行uop计数不是处理器宽度倍数的循环时性能是否会降低?

我想知道各种大小的循环如何在最近的x86处理器上执行,作为uop数的函数.

以下是彼得·科德斯(Peter Cordes)的一句话,他在另一个问题中提出了非多数的问题:

我还发现,如果循环不是4 uop的倍数,则循环缓冲区中的uop带宽不是每个循环的常数4.(即它是abc,abc,......;不是abca,bcab,......).遗憾的是,Agner Fog的microarch doc对循环缓冲区的这种限制并不清楚.

问题是关于循环是否需要是N uop的倍数才能以最大uop吞吐量执行,其中N是处理器的宽度.(即最近的英特尔处理器为4).在谈论"宽度"和计算微动时,有很多复杂因素,但我大多想忽略这些因素.特别是,假设没有微观或宏观融合.

Peter给出了以下一个循环,其中包含7个uop的循环:

一个7-uop循环将发出4 | 3 | 4 | 3 | ...的组我没有测试更大的循环(不适合循环缓冲区),看看是否有可能从下一个指令开始迭代发布在与其分支相同的组中,但我不假设.

更一般地说,声称是x在其体内具有uops 的循环的每次迭代将至少进行ceil(x / 4)迭代,而不是简单地迭代x / 4.

对于部分或全部最新的x86兼容处理器,这是真的吗?

performance x86 assembly cpu-architecture micro-optimization

20
推荐指数
2
解决办法
2048
查看次数

函数调用循环比空循环快

我将一些程序集与一些c链接起来测试函数调用的成本,使用以下程序集和c源代码(分别使用fasm和gcc)

部件:

format ELF

public no_call as "_no_call"
public normal_call as "_normal_call"

section '.text' executable

iter equ 100000000

no_call:
    mov ecx, iter
@@:
    push ecx
    pop ecx
    dec ecx
    cmp ecx, 0
    jne @b
    ret

normal_function:
    ret

normal_call:
    mov ecx, iter
@@:
    push ecx
    call normal_function
    pop ecx
    dec ecx
    cmp ecx, 0
    jne @b
    ret
Run Code Online (Sandbox Code Playgroud)

c来源:

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

extern int no_call();
extern int normal_call();

int main()
{
    clock_t ct1, ct2;

    ct1 = clock();
    no_call();
    ct2 = clock(); …
Run Code Online (Sandbox Code Playgroud)

c performance x86 assembly fasm

15
推荐指数
1
解决办法
969
查看次数

添加冗余分配可在编译时加速代码而无需优化

我发现了一个有趣的现象:

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

int main() {
    int p, q;
    clock_t s,e;
    s=clock();
    for(int i = 1; i < 1000; i++){
        for(int j = 1; j < 1000; j++){
            for(int k = 1; k < 1000; k++){
                p = i + j * k;
                q = p;  //Removing this line can increase running time.
            }
        }
    }
    e = clock();
    double t = (double)(e - s) / CLOCKS_PER_SEC;
    printf("%lf\n", t);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

我在i5-5257U Mac OS上使用GCC 7.3.0来编译代码 …

performance x86 assembly

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