函数调用在复合类中需要更长的时间
Function call takes longer in composite class
为了理解类组合的运行时性能,我编写了以下测试代码。在它中,我比较了直接作为类的成员函数调用函数所花费的时间,以及通过将原始类作为成员的复合类调用函数所花的时间。
这些方法似乎应该花费相当的时间,但事实并非如此:通过复合类进行调用几乎需要两倍的时间。
这是代码:
const int REPS(1e8);
const double INPUT(5.29);
class Base {
public:
inline double BaseFunc(double x) const;
};
double Base::BaseFunc(double x) const {
return 2.718*x + 3.14;
};
class Super {
public:
inline double BaseFunc(double x) const;
private:
Base b_;
};
double Super::BaseFunc(double x) const {
return b_.BaseFunc(x);
};
int main() {
auto t0 = std::chrono::high_resolution_clock::now();
// Construct objects.
Base b;
Super s;
// Call to base directly.
for (int i = 0; i < REPS; ++i)
b.BaseFunc(INPUT);
auto t1 = std::chrono::high_resolution_clock::now();
// Call to base through composited class.
for (int i = 0; i < REPS; ++i)
s.BaseFunc(INPUT);
auto t2 = std::chrono::high_resolution_clock::now();
// Find average durations.
auto diff1 = std::chrono::duration_cast<std::chrono::nanoseconds>(t1-t0).count();
diff1 /= REPS;
auto diff2 = std::chrono::duration_cast<std::chrono::nanoseconds>(t2-t1).count();
diff2 /= REPS;
std::cout << "Calling directly to base took " << diff1 << "nsec.n";
std::cout << "Calling to base through a composited class took " << diff2 << "nsec.n";
}
用g++版本4.7.2编译,用-std=c++11-O0-Winline,我得到:
Calling directly to base took 13nsec.
Calling to base through a composited class took 24nsec.
为什么这两种调用本质上相同函数的方式之间存在如此大的差异?我认为,由于所有内容都是内联的(gcc没有告诉我其他情况),所以它们应该是相同的。
我是不是完全错了?感谢您的帮助!谢谢
更新感谢您的帮助!我回去在函数调用中添加了更多内容(在向量上调用inner_product),并在每次重复时使用所有结果,以确保gcc没有优化任何内容。然后我打开了优化。你们都是对的:差别消失了。
我想我学到了两件重要的事情:1)在关闭优化的情况下,gcc甚至不会尝试内联,所以-Winline标志没有意义;2)这两种调用函数的方法之间没有任何有意义的区别。我可以自信地调用其他类中的成员函数!
再次感谢!
程序是未优化编译的(-O0
)。这意味着我们不能期望通常的代码质量。为了找出问题所在,我们需要查看生成的代码。可能没有发生内联(尽管是使用inline
请求的)。这可能使运行时调用的数量增加了一倍,因此大约使运行时间增加了一番。
如果程序得到了适当的优化,两个循环都将被完全删除。我认为这个基准是没有意义的,无论结果如何,它都毫无意义。在生产中,将执行全面的优化和现实的工作负载。
inline
只是编译器的提示,而不是要求。事实上,大多数编译器都忽略了它。
您提供了编译器参数-O0
,禁用了所有优化。由于内联是一种优化,所以它不会发生。这意味着调用开销会发生两次,这几乎解释了运行时间的两倍。
要查看函数调用是否内联,可以查看生成的汇编代码。在Linux上,您可以使用objdump -d
来执行此操作。如果你不想费力地完成所有的组装,如果你知道自己在寻找什么,你可以寻找有意义的东西:
objdump -d a.out | egrep "(main|BaseFunc|call)" | egrep "(main|BaseFunc)" | egrep -v "(jmp|jne)"
(是的,如果重写,egrep正则表达式链几乎肯定会缩短,但这不是重点。)
如果函数没有内联,结果将类似于以下内容:
08048700 <__libc_start_main@plt>:
804879c: e8 5f ff ff ff call 8048700 <__libc_start_main@plt>
080488c0 <main>:
804896e: e8 65 02 00 00 call 8048bd8 <_ZNK4Base8BaseFuncEd>
80489b5: e8 50 02 00 00 call 8048c0a <_ZNK5Super8BaseFuncEd>
08048bd8 <_ZNK4Base8BaseFuncEd>:
08048c0a <_ZNK5Super8BaseFuncEd>:
8048c30: e8 a3 ff ff ff call 8048bd8 <_ZNK4Base8BaseFuncEd>
请注意,在main
中,对名为BaseFunc()
的函数的两个调用(Base::BaseFunc()
和Super::BaseFunc()
)。
我把你的-O0
改成了-O1
,然后我看到了这样的东西。。。
08048660 <__libc_start_main@plt>:
80486dc: e8 7f ff ff ff call 8048660 <__libc_start_main@plt>
08048800 <main>:
现在,您可以判断出两个函数调用都是内联的,而且(在这种情况下,很可能)甚至REPS循环本身也可能被优化掉了,因为在我的系统上,两个结果都是0ns。
为了防止循环优化,我添加了这个全局变量:
volatile long gCtr = 0;
在函数Base::BaseFunc()
中,我将其添加到return
:之前
gCtr += 1;
现在两者的结果都是2ns,这似乎是可信的。两个BaseFunc()
函数都是内联的,但循环并没有被优化掉。
- C++为构建时间获取QDateTime的可靠方法
- 从持续时间构造std::chrono::system_clock::time_point
- 向量 <int> a {N, 0} 和 int arr a[N] = {0} 的时间复杂度有什么区别
- while循环中while循环的时间复杂度是多少
- 使用简单类型列表实现的指数编译时间.为什么
- 是否可以在编译时初始化数组,以便在运行时不会花费时间?
- 在已经使用Git的情况下减少编译时间
- 有没有一种方法可以创建一个带有哈希表的数据库,该哈希表具有恒定时间查找功能
- 如何将包含epoch时间的十六进制字符串转换为time_t
- 从文本文件中读取时钟时间和事件时间并进行处理
- 具有未知值时的时间复杂性
- 如何减少花费的时间
- C++在变量给定的指定时间内关闭电脑
- rcpp函数中的清理时间很长
- C++:floor unix时间戳到UTC月份
- 如何在c++中录制具有精确帧时间戳的视频
- 在两台机器之间进行时间戳的最佳c++chrono函数是什么
- KMP算法和LPS表构造的运行时间
- 时间复杂度 当具有复合数据类型(如元组或对)时?
- 函数调用在复合类中需要更长的时间