【发布时间】:2017-06-04 18:25:25
【问题描述】:
我最近修改了一个程序以使用虚函数(代替序列 if if-else 条件与静态调用。)修改后的程序运行速度比原来慢 8%。使用虚函数的成本似乎太高了,所以我在设置类层次结构和虚函数的方式上肯定做了一些低效的事情;但是,我不知道如何追查问题。 (在我的 Mac 上使用 clang 和在 Linux 上使用 gcc 时,我看到类似的性能下降。)
该程序用于研究不同的社区检测算法。该程序使用嵌套循环将一系列用户指定的目标函数应用于各种(图、分区)对。
这里是原始代码的大致轮廓
int main(int argc, char* argv[]) {
bool use_m1;
bool use_m2;
...
bool use_m10;
// set the various "use" flags based on argv
for (Graph& g : graphsToStudy()) {
for (Partition& p : allPartitions()) {
if (use_m1) {
M1::evaluate(g, p);
}
if (use_m2) {
M2::evaluate(g,p);
}
// and so on
}
}
为了使代码更易于维护,我为不同的目标函数创建了一个类结构,并迭代了一个指针数组:
class ObjectiveFunction {
public:
virtual double eval(Graph& g, Partition& p) = 0;
}
class ObjFn1 : public ObjectiveFunction {
public:
virtual double eval(Graph& g, Partition& p) {
return M1::evaluate(g,p);
}
}
class ObjFn2 : public ObjectiveFunction {
public:
virtual double eval(Graph& g, Partition& p) {
return M2::evaluate(g,p);
}
}
int main(int argc, char* argv[]) {
vector<ObjectiveFunction*> funcs;
fill_funcs_based_on_opts(funcs, argc, argv);
for (Graph& g : graphsToStudy()) {
for (Partition& p : allPartitions()) {
// funcs contains one object for each function selected by user.
for (ObjectiveFunction* fp : funcs) {
fp->evaluate(g, p);
}
}
}
鉴于生成图和分区以及目标函数本身的计算密集度适中,虚拟函数调用的添加几乎不会引起注意。任何我可能做错的想法;或者如何追踪它?我尝试使用 callgrind,但没有看到任何见解。
也许我只是错误地解释了callgrind_annotate 的输出。在下面的示例中,Neo::Context::evaluatePartition 类似于上面示例中的ObjFn1::evaluate。
为什么这个函数列出了四个不同的时间和不同的 源文件?此方法仅从函数
main调用 在timeMetrics.cpp。src/lib/PartitionIterator.h:main指的是什么?没有PartitionIterator.h中的main函数。为什么 414,219,420 在源代码列表中出现两次
evaluatePartition?不是第一个数字应该代表 函数调用的开销?
35,139,513,913 PROGRAM TOTALS
17,029,020,600 src/lib/metrics/Neo.h:gvcd::metrics::Neo::Context<unsigned int, unsigned char, unsigned int>::evaluatePartition(gvcd::Partition<unsigned int, unsigned int> const&, bool) [bin/timeMetrics_v]
7,168,741,865 /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/vector:gvcd::Partition<unsigned int, unsigned int>::buildMembersh ipList()
4,418,473,884 src/lib/Partition.h:gvcd::Partition<unsigned int, unsigned int>::buildMembershipList() [bin/timeMetrics_v]
1,459,239,657 src/lib/PartitionIterator.h:main
1,288,682,640 /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/vector:gvcd::metrics::Neo::Context<unsigned int, unsigned char, u nsigned int>::evaluatePartition(gvcd::Partition<unsigned int, unsigned int> const&, bool)
1,058,560,740 src/lib/Partition.h:gvcd::metrics::Neo::Context<unsigned int, unsigned char, unsigned int>::evaluatePartition(gvcd::Partition<unsigned int, unsigned int> const&, bool)
1,012,736,608 src/perfEval/timeMetrics.cpp:main [bin/timeMetrics_v] 443,847,782 /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/vector:main
368,372,912 /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/memory:gvcd::Partition<unsigned int, unsigned int>::buildMembersh ipList()
322,170,738 /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/ostream:main
92,048,760 src/lib/SmallGraph.h:gvcd::metrics::Neo::Context<unsigned int, unsigned char, unsigned int>::evaluatePartition(gvcd::Partition<unsigned int, unsigned int> const&, bool)
84,549,144 ???:szone_free_definite_size [/usr/lib/system/libsystem_malloc.dylib]
54,212,938 ???:tiny_free_list_add_ptr [/usr/lib/system/libsystem_malloc.dylib]
. virtual double
414,219,420 evaluatePartition(const Partition <VertexID, SetBitmap> &p, bool raw = false) {
414,219,420 uint_wn_t raw_answer = Neo::evaluatePartition(*(this->g), p);
. return (double) (raw ? raw_answer : max_neo - raw_answer);
. }
. }; // end Context
【问题讨论】:
-
告诉我你没有
#define foreach for... -
没有。这个例子不是来自我的真实代码,因为它有很多模板,所以要复杂得多。在回到 C++ 之前,我教了 Java 多年。旧习惯很难改掉:)
-
问题很可能是你用静态调用代替了分支预测器的好处,而动态调用是两个间接的。您的虚拟功能正在浪费 CPU 周期,而分支则没有。
-
也许不要复制不需要复制的东西?
-
你应该看到它。你认为
Graph g : graphsToStudy()在做什么?
标签: c++ c++11 performance-testing virtual-functions