c++ - 为什么编译器不优化琐碎的包装函数指针?
问题描述
考虑以下代码片段
#include <vector>
#include <cstdlib>
void __attribute__ ((noinline)) calculate1(double& a, int x) { a += x; };
void __attribute__ ((noinline)) calculate2(double& a, int x) { a *= x; };
void wrapper1(double& a, int x) { calculate1(a, x); }
void wrapper2(double& a, int x) { calculate2(a, x); }
typedef void (*Func)(double&, int);
int main()
{
std::vector<std::pair<double, Func>> pairs = {
std::make_pair(0, (rand() % 2 ? &wrapper1 : &wrapper2)),
std::make_pair(0, (rand() % 2 ? &wrapper1 : &wrapper2)),
};
for (auto& [a, wrapper] : pairs)
(*wrapper)(a, 5);
return pairs[0].first + pairs[1].first;
}
使用 -O3 优化,最新的 gcc 和 clang 版本不会优化指向包装器的指针,指向指向底层函数的指针。请参见第 22 行的程序集:
mov ebp, OFFSET FLAT:wrapper2(double&, int) # tmp118,
这会导致稍后出现call
+ jmp
,而不是call
让编译器放置一个指向 the 的指针calculate1
。
请注意,我特别要求使用非内联calculate
函数来说明;这样做会noinline
导致另一种非优化,编译器将生成两个相同的函数以通过指针调用(因此仍然不会优化,只是以不同的方式)。
我在这里想念什么?有没有办法指导编译器手动插入正确的函数(没有包装器)?
编辑 1.按照评论中的建议,这是一个反汇编,所有函数都声明为静态,结果完全相同(call
+jmp
而不是call
)。
编辑 2.相同模式的更简单示例:
#include <vector>
#include <cstdlib>
typedef void (*Func)(double&, int);
static void __attribute__ ((noinline)) calculate(double& a, int x) { a += x; };
static void wrapper(double& a, int x) { calculate(a, x); }
int main() {
double a = 5.0;
Func f;
if (rand() % 2)
f = &wrapper; // f = &calculate;
else
f = &wrapper;
f(a, 0);
return 0;
}
gcc 8.2 通过将指向包装器的指针扔掉并&calculate
直接存储在其位置(https://gcc.godbolt.org/z/nMIBeo)成功地优化了此代码。但是,根据注释更改行(即手动执行部分相同优化)会破坏魔术并导致无意义的结果jmp
。
解决方案
您似乎建议&calculate1
应该将其存储在 vector 而不是&wrapper1
. 通常这是不可能的:稍后的代码可能会尝试将存储的指针&calculate1
与它进行比较,并且必须比较为假。
我进一步假设您的建议是编译器可能会尝试进行一些静态分析并确定向量中的函数指针值永远不会与其他函数指针的相等性进行比较,事实上,没有对向量进行任何其他操作元素会产生可观察行为的变化;因此在这个确切的程序中它可以存储&calculate1
。
通常,“为什么编译器不执行某些特定优化”的答案是没有人想到并实现这个想法。另一个常见的原因是,在一般情况下,所涉及的静态分析非常困难,并且可能导致编译速度变慢,而在无法保证分析成功的实际程序中没有任何好处。
推荐阅读
- python - Django根据不同的用户组部分限制内容
- django - “Q”对象没有属性“order_by”-Django
- jquery - JQuery - 显示多个选定值下拉列表
- node.js - 我可以从 vscode 启动远程节点进程吗?
- angular5 - 使用 Angular 4-5-6 在网络和移动设备中打开共享对话框
- javascript - 使用 iText 在 pdf 的特定位置上盖章
- python - 在 Pygame 中继续之前等待 keydown
- sql - SQL:如何检查列中是否包含字母
- java - 测试用例java中的骆驼确认
- cluster-analysis - Mahout 聚类 - 单个聚类中的所有文本向量 - 为什么?