请考虑以下代码:
void error_handling();
bool method_impl();
bool method()
{
const bool res = method_impl();
if (res == false) {
error_handling();
return false;
}
return true;
}
Run Code Online (Sandbox Code Playgroud)
我知道当时method_impl()会返回true99.999%(是的,三位小数),但我的编译器没有.method()在时间消耗方面是部分关键.
method()(并使其不太可读)以确保只有在method_impl()返回时才会发生跳转false?如果有,怎么样?我发现了这个非常漂亮的信息图,它对某些操作所使用的 CPU 周期进行了粗略估计。在学习时,我注意到一个条目“if的右分支”,我认为如果满足条件,该分支将采用“if”(编辑:正如评论中指出的“右”实际上意味着“正确预测的分支” )。这让我想知道 if 分支与 else 分支相比是否存在任何(即使是很小的)速度差异。
例如,比较以下非常简洁的代码:
#include <cstdio>
volatile int a = 2;
int main()
{
if (a > 5) {
printf("a > 5!");
a = 2;
} else {
printf("a <= 5!");
a = 3;
}
}
Run Code Online (Sandbox Code Playgroud)
它在 x86 64 位中生成此程序集:
.LC0:
.string "a > 5!"
.LC1:
.string "a <= 5!"
main:
push rcx
mov eax, DWORD PTR a[rip]
cmp eax, 5
jle .L2
mov edi, OFFSET FLAT:.LC0
xor eax, eax …Run Code Online (Sandbox Code Playgroud) 当我们编译代码并执行它时,在汇编中,我们的代码被转换,函数以非顺序方式存储.因此,每次调用函数时,处理器都需要丢弃管道中的指令.这不会影响程序的性能吗?
PS:我没有考虑投入时间来开发没有功能的程序.纯粹在性能水平上.编译器是否有任何方法可以解决这个问题?
您是否可以有意识地以特定方式编写代码,以便分支预测器将选择大多数情况下的选项.例如,错误检查资源是否已加载.如果可能的话,你如何利用这个优势?
假设我有这样的代码,称之为版本 1:
while (some_condition) {
// .. work that may trigger rare_condition ...
if (rare_condition) {
// .. rare work here ..
continue;
}
// .. work that may trigger rare_condition ...
if (rare_condition) {
// .. rare work here ..
continue;
}
// .. more work
}
Run Code Online (Sandbox Code Playgroud)
假设这两种情况下的“稀有作品”是相同的。我们可以等效地编写版本 2:
while (some_condition) {
// .. work that may trigger rare_condition ...
if (rare_condition) {
goto rare_work;
}
// .. work that may trigger rare_condition ...
if (rare_condition) {
goto rare_work; …Run Code Online (Sandbox Code Playgroud) 我们正在研究一种模型检查工具,它可以执行几十亿次搜索程序.我们有不同的搜索例程,目前使用预处理器指令进行选择.这不仅非常不方便,因为我们每次做出不同的选择时都需要重新编译,但也使代码难以阅读.现在是开始新版本的时候了,我们正在评估是否可以避免条件编译.
这是一个非常人为的例子,显示了效果:
/* program_define */
#include <stdio.h>
#include <stdlib.h>
#define skip 10
int main(int argc, char** argv) {
int i, j;
long result = 0;
int limit = atoi(argv[1]);
for (i = 0; i < 10000000; ++i) {
for (j = 0; j < limit; ++j) {
if (i + j % skip == 0) {
continue;
}
result += i + j;
}
}
printf("%lu\n", result);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
这里,变量skip是影响程序行为的值的示例.不幸的是,我们需要在每次需要新值时重新编译skip.
让我们看看该程序的另一个版本:
/* program_variable */
#include …Run Code Online (Sandbox Code Playgroud) 这是一个对任何平台、语言或编译器都开放的天真的一般性问题。虽然我最好奇的是 Aarch64、C++、GCC。
当在依赖于 I/O 状态的程序流中编写不可避免的分支时(编译器无法预测),并且我知道一种状态比另一种状态更有可能,我如何向编译器表明这一点?
这是否更好
if(true == get(gpioVal))
unlikelyFunction();
else
likelyFunction();
Run Code Online (Sandbox Code Playgroud)
比这个?
if(true == get(gpioVal))
likelyFunction(); // performance critical, fill prefetch caches from this branch
else
unlikelyFunction(); // missed prediction not consequential on this branch
Run Code Online (Sandbox Code Playgroud)
如果通信协议使更有可能或临界值为真(高)或假(低),这是否有帮助?
在 Visual Studio 中阅读标准库算法的实现时,他们根本不使用[[likely]]/属性。[[unlikely]]对我来说,应该使用的教科书示例[[unlikely]]是例如std::find_if(...),微软已经这样实现:
// Note that some noise are removed from the code
template <class _InIt, class _Pr>
_InIt find_if(_InIt _First, const _InIt _Last, _Pr _Pred) {
for (; _First != _Last; ++_First) {
if (_Pred(*_First)) {
break;
}
}
return _First;
}
Run Code Online (Sandbox Code Playgroud)
如前所述,find_if(...)这是 if 子句的 true 分支不太可能的教科书示例,因为大多数情况下它会在谓词验证为 true 之前迭代几个元素,因此 [[unlikely]] 将是一个优化机会。Microsoft 没有[[unlikely]]在这里使用该属性有什么原因吗?
c++ algorithm micro-optimization compiler-optimization likely-unlikely
我正在编写一个简单的汇编程序,当然,它的目的是尽可能快.但是,位于最嵌套循环中的某个部分看起来并不"正确",我相信有可能提出更聪明,更快速的实现,甚至可能不使用条件跳转.代码实现了一个简单的事情:
if rax < 0 then
rax := 0
else if rax >= r12 then
rax := r12 - 1
这是我天真的实施:
cmp rax, 0
jge offsetXGE
mov rax, 0
jmp offsetXReady
offsetXGE:
cmp rax, r12
jl offsetXReady
mov rax, r12
dec rax
offsetXReady:
Run Code Online (Sandbox Code Playgroud)
任何想法都是受欢迎的,即使是那些使用MMX和一些掩盖技巧的想法.
编辑:回答评论中的一些问题 - 是的,我们可以假设r12> 0但rax可能是负数.
下面是一些 C++ 伪代码作为示例:
bool importantFlag = false;
for (SomeObject obj : arr) {
if (obj.someBool) {
importantFlag = true;
}
obj.doSomethingUnrelated();
}
Run Code Online (Sandbox Code Playgroud)
显然,一旦 if 语句评估为 true 并运行内部代码,就没有理由再次执行检查,因为无论哪种方式结果都是相同的。编译器是否足够聪明,能够识别这一点,还是会在每次循环迭代中继续检查 if 语句,并可能再次将 importantFlag 冗余地分配为 true?如果循环迭代次数很大,并且无法跳出循环,则这可能会对性能产生显着影响。
我通常会忽略这些情况,只是将信心寄托在编译器上,但如果能确切地知道它如何处理这些情况,那就太好了。