【发布时间】:2012-02-13 08:56:46
【问题描述】:
我们正在开发一种模型检查工具,它可以执行某些搜索例程数十亿次。我们有不同的搜索例程,当前使用预处理器指令选择这些例程。这不仅非常不方便,因为我们每次做出不同的选择时都需要重新编译,而且还会使代码难以阅读。现在是开始新版本的时候了,我们正在评估是否可以避免条件编译。
这是一个非常人为的例子,展示了效果:
/* 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;
}
这里,变量skip 是一个影响程序行为的值的示例。不幸的是,每次我们需要skip 的新值时,我们都需要重新编译。
让我们看看另一个版本的程序:
/* program_variable */
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char** argv) {
int i, j;
long result = 0;
int limit = atoi(argv[1]);
int skip = atoi(argv[2]);
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;
}
这里,skip 的值作为命令行参数传递。这增加了很大的灵活性。但是,这个程序要慢得多:
$ time ./program_define 1000 10
50004989999950500
real 0m25.973s
user 0m25.937s
sys 0m0.019s
对比
$ time ./program_variable 1000 10
50004989999950500
real 0m50.829s
user 0m50.738s
sys 0m0.042s
我们正在寻找一种将值传递给程序(通过命令行参数或文件输入)的有效方法,该方法之后将永远不会改变。有没有办法优化代码(或告诉编译器)以使其更有效地运行?
非常感谢任何帮助!
评论:
正如 Dirk 在他的 comment 中所写,这与具体示例无关。我的意思是一种替换if 的方法,该if 评估一个设置一次然后永远不会更改(例如命令行选项)的变量,该函数在字面上被 数十亿次 调用更有效的构造。我们目前使用预处理器来定制所需的函数版本。如果有更好的不需要重新编译的方法就好了。
【问题讨论】:
-
也许你不应该使用不断改变和重新格式化你的代码的预处理指令?
-
有趣的是,这两个示例的性能在我的机器上几乎无法区分(64 位 Ubuntu,
gcc4.4.3 和-O3)。 -
差异几乎可以肯定是因为如果
skip在编译时已知,编译器可以只使用乘法和移位为j % skip生成更好的代码。 -
坏消息是在优化器不知道确切值的情况下,除法(并且
%算作除法)通常会变慢。如果效果更快,您可以玩一些技巧,用一个更多的乘法/加法/移位代替特定值的除法。原则上,如果您在优化之前有合适的中间形式,则不必重新编译整个程序以重新运行优化器以获得新值。您可以尝试使用带有链接时优化的编译器,看看它是否可以在您重新链接新值时进行优化。 -
哦,这听起来可能很奇怪,但您可以在 C# 或 Java 中尝试一下。由于它们是 JIT 编译的,它们比典型的 C++ 工具链优化得晚,所以你可能会很幸运。诀窍是在编译到字节码之后,但在优化代码之前,弄清楚如何提供值。
标签: c conditional-compilation optimization c-preprocessor