C++ 如何启用Visual Studio C++;分支优化?

C++ 如何启用Visual Studio C++;分支优化?,c++,visual-c++,compiler-optimization,micro-optimization,branch-prediction,C++,Visual C++,Compiler Optimization,Micro Optimization,Branch Prediction,我有两个功能,下面的功能在执行方面应该是相同的。唯一的区别是,在第二个函数funcB()中,我将(decobj==NULL)上的分支移到了始终为NULL的循环之外。我认为编译器可以很容易地优化funcA(),使其具有与FuncB()相同的执行时间,但事实并非如此,在执行时间上存在很大差异: FuncA took: 3644530 uS FuncB took: 1664598 uS FuncB took: 1626528 uS FuncA took: 3108780 uS Finished! 我

我有两个功能,下面的功能在执行方面应该是相同的。唯一的区别是,在第二个函数funcB()中,我将(decobj==NULL)上的分支移到了始终为NULL的循环之外。我认为编译器可以很容易地优化funcA(),使其具有与FuncB()相同的执行时间,但事实并非如此,在执行时间上存在很大差异:

FuncA took: 3644530 uS
FuncB took: 1664598 uS
FuncB took: 1626528 uS
FuncA took: 3108780 uS
Finished!
我使用的是Visual Studio 2019(预览版),编译后的x64已启用完全优化:

有人知道如何让Visual Studio优化funcA(),使其与funcB()一样快吗

#包括
#包括
使用名称空间std;
结构解码{
整数解码(整数i,整数j){
返回i+j;
}
};
静态解码robj*decobj=nullptr;
静态常数int迭代次数=3000;
void funcA(){
自动启动=时钟::高分辨率时钟::现在();
对于(int rep=0;rep解码(i,j):i-j;
如果(a>99999){

你到底有什么CPU?(例如i7-6700k Skylake)。代码对齐效果可能会对“decobj为空”造成一些不好的影响循环,例如,为一个循环跨越32B的边界,否则它将是1次迭代/时钟。或者-JCC勘误表在微码中引入了一个性能缺陷,编译器可能还不能解决。你是否查看了汇编输出,以了解编译器在做什么?@PeterCordes我正在核心i5-8350U CPU上运行这个。我的想法无论是使用“decobj!=nullptr”语句还是不使用它,编译器都应该意识到“if(decobj!=nullptr)”永远不会被执行,因此永远不会为它生成代码。我也将这一行添加到funcA()中,但令人惊讶的是它减慢了funcB()而不是加快了funcA()。编译器应该能够在该行存在的情况下轻松优化这两个函数。应该能够在没有该行的情况下进行优化,但肯定是使用该行。是的,您希望这对MSVC来说也是显而易见的,尤其是对于最近的版本,但您必须检查asm以确保。例如,将其安装到或在本地询问编译器输出asm而不是机器代码。(或编译后反汇编)。首先要弄清楚它为什么慢,不管是一个微体系结构的凹坑,还是一堆工作,然后再弄清楚这些工作是否应该被优化掉。Clang对这两种方法都有相同的性能,因为它生成相同的程序集…你到底有什么CPU?(例如i7-6700k Skylake).Code alignment effects可能对“decobj为NULL”做了一些不幸的事情循环,例如,为一个循环跨越32B的边界,否则它将是1次迭代/时钟。或者-JCC勘误表在微码中引入了一个性能缺陷,编译器可能还不能解决。你是否查看了汇编输出,以了解编译器在做什么?@PeterCordes我正在核心i5-8350U CPU上运行这个。我的想法无论是使用“decobj!=nullptr”语句还是不使用它,编译器都应该意识到“if(decobj!=nullptr)”永远不会被执行,因此永远不会为它生成代码。我也将这一行添加到funcA()中,但令人惊讶的是它减慢了funcB()而不是加快了funcA()。编译器应该能够在该行存在的情况下轻松优化这两个函数。应该能够在没有该行的情况下进行优化,但肯定是使用该行。是的,您希望这对MSVC来说也是显而易见的,尤其是对于最近的版本,但您必须检查asm以确保。例如,将其安装到或在本地询问编译器输出asm而不是机器代码。(或编译后反汇编)。首要任务是弄清楚它为什么慢,是一个微体系结构的凹坑,还是一堆工作,然后看看这些工作是否应该被优化掉。Clang对这两种方法都有相同的性能,因为它生成相同的程序集。。。
#include <iostream>
#include <chrono>

using namespace std;

struct DecoderObj {
    int decode(int i, int j) {
        return i + j;
    }
};

static DecoderObj* decobj = nullptr;
static const int iterations = 3000;

void funcA() {
    auto start = chrono::high_resolution_clock::now();
    for (int rep = 0; rep < iterations; rep++) {
        for (int i = 0; i < 999; i++) {
            for (int j = 0; j < 999; j++) {
                int a = decobj ? decobj->decode(i, j) : i - j;
                if (a > 99999) {
                    cout << "This should never print";
                    return;
                }
            }
        }
    }
    auto end = chrono::high_resolution_clock::now();
    cout << "FuncA took: " << chrono::duration_cast<chrono::microseconds>(end - start).count() << " uS" << endl;
}

void funcB() {
    auto start = chrono::high_resolution_clock::now();
    for (int rep = 0; rep < iterations; rep++) {
        if (decobj != nullptr) {
            for (int i = 0; i < 999; i++) {
                for (int j = 0; j < 999; j++) {
                    int a = decobj->decode(i, j);
                    if (a > 99999) {
                        cout << "This should never print";
                        return;
                    }
                }
            }
        }
        else { //decobj is NULL
            for (int i = 0; i < 999; i++) {
                for (int j = 0; j < 999; j++) {
                    int a = i - j;
                    if (a > 99999) {
                        cout << "This should never print";
                        return;
                    }
                }
            }
        }
    }
    auto end = chrono::high_resolution_clock::now();
    cout << "FuncB took: " << chrono::duration_cast<chrono::microseconds>(end - start).count() << " uS" << endl;
}

int main() {
    funcA();
    funcB();
    funcB();
    funcA();

    std::cout << "Finished!\n";
}
; 17   :     for (int rep = 0; rep < iterations; rep++) {

    mov r9, QWORD PTR ?decobj@@3PEAUDecoderObj@@EA
    xor r10d, r10d
    npad    6
$LL4@funcA:

; 18   :         for (int i = 0; i < 999; i++) {

    xor r8d, r8d
    npad    13
$LL7@funcA:

; 19   :             for (int j = 0; j < 999; j++) {

    xor eax, eax
    mov edx, r8d
$LL10@funcA:

; 20   :                 int a = decobj ? decobj->decode(i, j) : i - j;

    lea ecx, DWORD PTR [rax+r8]
    test    r9, r9
    jne SHORT $LN14@funcA
    mov ecx, edx
$LN14@funcA:

; 21   :                 if (a > 99999) {

    cmp ecx, 99999              ; 0001869fH
    jg  SHORT $LN20@funcA

; 19   :             for (int j = 0; j < 999; j++) {

    inc eax
    dec edx
    cmp eax, 999                ; 000003e7H
    jl  SHORT $LL10@funcA

; 18   :         for (int i = 0; i < 999; i++) {

    inc r8d
    cmp r8d, 999                ; 000003e7H
    jl  SHORT $LL7@funcA

; 16   :     //auto start = chrono::high_resolution_clock::now();
; 17   :     for (int rep = 0; rep < iterations; rep++) {

    inc r10d
    cmp r10d, 3000              ; 00000bb8H
    jl  SHORT $LL4@funcA

; 23   :                     return;
; 24   :                 }
; 25   :             }
; 26   :         }
; 27   :     }
; 28   :     //auto end = chrono::high_resolution_clock::now();
; 29   :     //cout << "FuncA took: " << chrono::duration_cast<chrono::microseconds>(end - start).count() << " uS" << endl;
; 30   : }

    ret 0
$LN20@funcA:

; 22   :                     cout << "This should never print";

    mov rcx, QWORD PTR __imp_?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A
    lea rdx, OFFSET FLAT:??_C@_0BI@DPJNIIKO@This?5should?5never?5print@
    jmp ??$?6U?$char_traits@D@std@@@std@@YAAEAV?$basic_ostream@DU?$char_traits@D@std@@@0@AEAV10@PEBD@Z ; std::operator<<<std::char_traits<char> >
?funcA@@YAXXZ ENDP                  ; funcA
_TEXT   SEGMENT
?funcB@@YAXXZ PROC                  ; funcB, COMDAT

; 33   :     //auto start = chrono::high_resolution_clock::now();
; 34   :     for (int rep = 0; rep < iterations; rep++) {

    mov r9, QWORD PTR ?decobj@@3PEAUDecoderObj@@EA
    xor r8d, r8d
    npad    6
$LL4@funcB:

; 35   :         if (decobj != nullptr) {

    xor edx, edx
    test    r9, r9
    je  SHORT $LL13@funcB
    npad    9
$LL7@funcB:

; 37   :                 for (int j = 0; j < 999; j++) {

    xor eax, eax
$LL10@funcB:

; 38   :                     int a = decobj->decode(i, j);
; 39   :                     if (a > 99999) {

    lea ecx, DWORD PTR [rax+rdx]
    cmp ecx, 99999              ; 0001869fH
    jg  SHORT $LN30@funcB

; 37   :                 for (int j = 0; j < 999; j++) {

    inc eax
    cmp eax, 999                ; 000003e7H
    jl  SHORT $LL10@funcB

; 36   :             for (int i = 0; i < 999; i++) {

    inc edx
    cmp edx, 999                ; 000003e7H
    jl  SHORT $LL7@funcB

; 40   :                         cout << "This should never print";
; 41   :                         return;
; 42   :                     }
; 43   :                 }
; 44   :             }
; 45   :         }

    jmp SHORT $LN2@funcB
$LL13@funcB:

; 48   :                 for (int j = 0; j < 999; j++) {

    xor ecx, ecx
    mov eax, edx
$LL16@funcB:

; 49   :                     int a = i - j;
; 50   :                     if (a > 99999) {

    cmp eax, 99999              ; 0001869fH
    jg  SHORT $LN30@funcB

; 48   :                 for (int j = 0; j < 999; j++) {

    inc ecx
    dec eax
    cmp ecx, 999                ; 000003e7H
    jl  SHORT $LL16@funcB

; 46   :         else { //decobj is NULL
; 47   :             for (int i = 0; i < 999; i++) {

    inc edx
    cmp edx, 999                ; 000003e7H
    jl  SHORT $LL13@funcB
$LN2@funcB:

; 33   :     //auto start = chrono::high_resolution_clock::now();
; 34   :     for (int rep = 0; rep < iterations; rep++) {

    inc r8d
    cmp r8d, 3000               ; 00000bb8H
    jge SHORT $LN3@funcB
    jmp SHORT $LL4@funcB
$LN30@funcB:

; 51   :                         cout << "This should never print";
; 52   :                         return;
; 53   :                     }
; 54   :                 }
; 55   :             }
; 56   :         }
; 57   :     }
; 58   :    // auto end = chrono::high_resolution_clock::now();
; 59   :     //cout << "FuncB took: " << chrono::duration_cast<chrono::microseconds>(end - start).count() << " uS" << endl;
; 60   : }

    mov rcx, QWORD PTR __imp_?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A
    lea rdx, OFFSET FLAT:??_C@_0BI@DPJNIIKO@This?5should?5never?5print@
    jmp ??$?6U?$char_traits@D@std@@@std@@YAAEAV?$basic_ostream@DU?$char_traits@D@std@@@0@AEAV10@PEBD@Z ; std::operator<<<std::char_traits<char> >
$LN3@funcB:
    ret 0
?funcB@@YAXXZ ENDP                  ; funcB
_TEXT   ENDS