“可能(真实)”属性的意义是什么 我刚刚读了一些关于C++中的属性的东西。他们在那里提到了probablytrue属性,现在我想知道它有什么好处。不幸的是,我在网上找不到更多的信息

“可能(真实)”属性的意义是什么 我刚刚读了一些关于C++中的属性的东西。他们在那里提到了probablytrue属性,现在我想知道它有什么好处。不幸的是,我在网上找不到更多的信息,c++,attributes,C++,Attributes,这是处理器在执行期间使用的某种分支预测吗 没错。它用于向编译器提供有关if语句的更多信息,以便编译器能够根据目标微体系结构生成最佳代码 虽然每个微体系结构都有自己的方法来了解分支的可能性,但我们可以从《英特尔优化手册》中举一个简单的例子 汇编/编译器编码规则3。M影响,H一般性安排代码与 静态分支预测算法:使遵循条件分支的故障代码成为 具有正向目标的分支的可能目标,并在条件 分支可能是具有向后目标的分支的不太可能的目标 简单地说,不采用前向分支的静态预测,因此分支后的代码是推测执行的,这是可能的

这是处理器在执行期间使用的某种分支预测吗

没错。它用于向编译器提供有关if语句的更多信息,以便编译器能够根据目标微体系结构生成最佳代码

虽然每个微体系结构都有自己的方法来了解分支的可能性,但我们可以从《英特尔优化手册》中举一个简单的例子

汇编/编译器编码规则3。M影响,H一般性安排代码与 静态分支预测算法:使遵循条件分支的故障代码成为 具有正向目标的分支的可能目标,并在条件 分支可能是具有向后目标的分支的不太可能的目标

简单地说,不采用前向分支的静态预测,因此分支后的代码是推测执行的,这是可能的路径,而采用后向分支的静态预测,因此分支后的代码不是推测执行的

考虑GCC的以下代码:

#define probably_true(x) __builtin_expect(!!(x), 1)
#define probably_false(x) __builtin_expect(!!(x), 0)

int foo(int a, int b, int c)
{
    if (probably_true(a==2))
        return a + b*c;
    else
        return a*b + 2*c;
}
在这里,我使用内置函数来模拟[[ProbablyTrue]]

这将被编译成

foo(int, int, int):
        cmp     edi, 2           ;Compare a and 2
        jne     .L2              ;If not equals jumps to .L2

        ;This is the likely path (fall-through of a forward branch)

        ;return a + b*c;

.L2:
        ;This is the unlikely path (target of a forward branch)

        ;return a*b + 2*c;

        ret
我给了你一些汇编代码。 如果将“可能为真”替换为“可能为假”,则代码将变为:

foo(int, int, int):
        cmp     edi, 2           ;Compare a and 2
        je     .L5               ;If equals jumps to .L5

        ;This is the likely path (fall-through of a forward branch)

        ;return a*b + 2*c;

.L5:
        ;This is the unlikely path (target of a forward branch)

        ;return a + b*c;

        ret

你可以玩。

是的,没错。它用于向编译器提供有关if语句的更多信息,以便编译器能够根据目标微体系结构生成最佳代码

虽然每个微体系结构都有自己的方法来了解分支的可能性,但我们可以从《英特尔优化手册》中举一个简单的例子

汇编/编译器编码规则3。M影响,H一般性安排代码与 静态分支预测算法:使遵循条件分支的故障代码成为 具有正向目标的分支的可能目标,并在条件 分支可能是具有向后目标的分支的不太可能的目标

简单地说,不采用前向分支的静态预测,因此分支后的代码是推测执行的,这是可能的路径,而采用后向分支的静态预测,因此分支后的代码不是推测执行的

考虑GCC的以下代码:

#define probably_true(x) __builtin_expect(!!(x), 1)
#define probably_false(x) __builtin_expect(!!(x), 0)

int foo(int a, int b, int c)
{
    if (probably_true(a==2))
        return a + b*c;
    else
        return a*b + 2*c;
}
在这里,我使用内置函数来模拟[[ProbablyTrue]]

这将被编译成

foo(int, int, int):
        cmp     edi, 2           ;Compare a and 2
        jne     .L2              ;If not equals jumps to .L2

        ;This is the likely path (fall-through of a forward branch)

        ;return a + b*c;

.L2:
        ;This is the unlikely path (target of a forward branch)

        ;return a*b + 2*c;

        ret
我给了你一些汇编代码。 如果将“可能为真”替换为“可能为假”,则代码将变为:

foo(int, int, int):
        cmp     edi, 2           ;Compare a and 2
        je     .L5               ;If equals jumps to .L5

        ;This is the likely path (fall-through of a forward branch)

        ;return a*b + 2*c;

.L5:
        ;This is the unlikely path (target of a forward branch)

        ;return a + b*c;

        ret

你可以玩。

这正是它的本来面目-看到硬件分支预测它只是一个仅适用于条件分支的属性的示例,就我所知,它实际上并不以那种形式存在我不确定我是否正确理解你,但我只是用以下语法编译了一些代码:[[probablytrue]]如果。。。其他的我没有检查生成的代码,但它至少使用MSC和C++编译过。14@Timo正如您链接到的cppreference页面所说,忽略了实现未知的所有属性,而不会导致错误该规则是为C++17添加的,但它只是强制执行了先前的意图和实践,这里有一个早期的建议来添加这些:-它只是简单地建议[[可能]]和[[不可能]]它正是它的本来面目-看到硬件分支预测它只是一个仅适用于条件分支的属性的示例,据我所知,它并不是以那种形式存在的。我不确定我是否理解正确,但我只是用以下语法编译了一些代码:[[probablytrue]]如果。。。其他的我没有检查生成的代码,但它至少使用MSC和C++编译过。14@Timo正如您链接到的cppreference页面所说,忽略了实现未知的所有属性,而不会导致错误该规则是为C++17添加的,但它只是强制执行了先前的意图和实践,这里有一个早期的建议来添加这些:-它只是建议[[可能]]和[[不可能]]@Timo它取决于体系结构或者更好的微体系结构,在x86中,对于静态预测的向前跳跃,你的说法是正确的,但只有在第一次看到分支时。在第一次之后,分支预测器尝试检测分支的已采取未采取行为的模式。关于起点,请参阅。尽管有《体系结构指南》中的建议,但据我所知,在现代x86上没有更多的静态预测规则!。也就是说,按照现代分支预测器的设计方式,永远只会有一个动态预测:如果没有看到分支,则预测是错误的
st基于散列的其他跳转在预测表中留下的任何垃圾,或者如果没有任何内容,则默认值可能会一直通过预测。从根本上说,静态预测规则来得太晚了-在您解码指令时,您已经获取了多行附加指令。因此,这也是静态预测基本上被放弃的另一个原因。动态预测机制可以在取指令之前工作。内置的expect仍然很有用,因为它有助于代码生成——最有可能的路径最好是直线路径,一般来说,最有可能的基本块可以组合在一起。不管怎么说,这与英特尔的老建议基本吻合@玛格丽特·布鲁姆——可能是,我现在也记不起我在哪里读过它。奇怪的是,在使用您的示例时,我发现icc产生了糟糕的代码,至少在我尝试使用的示例中,如果您使用uuu builtin_expect。从生成for this循环到gcc和clang循环,两者都失败并生成带有5条指令的循环,再到完全糟糕的代码,无论您预测退出。它添加了cmov和其他垃圾,可能是由于!!x部分?@BeeOnRope我不能确定,但可能是,第一个指令似乎设置了eax=!!esi@Timo这取决于体系结构,或者更好的是微观体系结构,在x86中,对于静态预测的前向跳跃,不认为您的陈述是正确的,但只有在第一次看到分支时。在第一次之后,分支预测器尝试检测分支的已采取未采取行为的模式。关于起点,请参阅。尽管有《体系结构指南》中的建议,但据我所知,在现代x86上没有更多的静态预测规则!。也就是说,按照现代分支预测器的设计方式,永远只会有一个动态预测:如果没有看到分支,那么预测只会基于预测表中散列的其他跳转留下的垃圾,或者,如果根本不存在任何预测,那么默认值可能总是无法通过预测。从根本上说,静态预测规则来得太晚了——当您解码指令时,您已经获取了多行附加指令。因此,这也是静态预测基本上被放弃的另一个原因。动态预测机制可以在取指令之前工作。内置的expect仍然很有用,因为它有助于代码生成——最有可能的路径最好是直线路径,一般来说,最有可能的基本块可以组合在一起。不管怎么说,这与英特尔的老建议基本吻合@玛格丽特·布鲁姆——可能是,我现在也记不起我在哪里读过它。奇怪的是,在使用您的示例时,我发现icc产生了糟糕的代码,至少在我尝试使用的示例中,如果您使用uuu builtin_expect。从生成for this循环到gcc和clang循环,两者都失败并生成带有5条指令的循环,再到完全糟糕的代码,无论您预测退出。它添加了cmov和其他垃圾,可能是由于!!x部分?@BeeOnRope我不能确定,但可能是,第一个指令似乎设置了eax=!!esi