最近看了 effective c++
这本书,书中有一种用模板元编程计算阶乘的骚操作,说是可以将计算从运行时转到编译期间,这样可以提高代码的执行效率。
但我尝试了下,发现并没有比使用 for 循环计算阶乘的方法快,反而花费了更多的时间,代码如下:
#include <chrono> #include<cstdlib> #include <iostream> #include <new> #include <vector> using std::size_t; template <unsigned x> struct fac { static const size_t value = x * fac<x - 1>::value; }; template <> struct fac<1> { static const size_t value = 1; }; // for 循环计算阶乘 size_t fori(size_t v) { size_t tmp = 1; for (size_t i = 1; i <= v; i++) { tmp = tmp * i; } return tmp; } // 利用模板元编程计算阶乘 constexpr size_t facc() { return fac<901>::value; } void func() { // 模板元编程计算耗时 auto start = std::chrono::high_resolution_clock::now(); constexpr auto tmp = facc(); std::cout << tmp << std::endl; auto end = std::chrono::high_resolution_clock::now(); auto duration = std::chrono::duration_cast<std::chrono::nanoseconds>(end - start); std::cout << "Elapsed time: " << duration.count() << " ns" << std::endl; // 输出 57466 ns // for 循环计算耗时 auto start1 = std::chrono::high_resolution_clock::now(); size_t t = fori(901); std::cout << tmp << std::endl; auto end1 = std::chrono::high_resolution_clock::now(); auto duration1 = std::chrono::duration_cast<std::chrono::nanoseconds>(end1 - start1); std::cout << "Elapsed time: " << duration1.count() << " ns" << std::endl; // 输出 1647 ns } int main() { func(); }
如果去掉打印,反而是利用模板元编程的更快,确实符合编译期计算提高效率的说法,但这里打印了其结果,反而花费了更多时间。
这是为什么,是编译器自个的优化策略问题还是什么?
编译器版本:gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0
1 zcion OP for 循环打印的结果写错了,不过不影响想要表达的意思 |
![]() | 2 bfjm 304 天前 调换了一下顺序,前大后小, 应该是程序冷启动的偏差 |
![]() | 3 bfjm 304 天前 ![]() 可以重复测试取中位数 会更精确一点 |
5 billccn 304 天前 一是你这样做 micro benchmark 完全不准确,因为程序运行会受到操作系统和 CPU 频率等诸多影响,需要使用 google bench 等框架测量才有意义。 二是 constexpr auto tmp 的值在编译的时候就算好了,不会耗时的,你这个代码主要测量了打印到 cout 和取高精度时间的开销。这些都是 syscall ,响应的耗时是很随机的。 |
6 vvhy 304 天前 不要把 cout 放在计时里,估计占 99%的时间 |
![]() | 7 ipwx 304 天前 只有我想吐槽,900 个数连乘,肯定已经溢出了么。。。 |
11 kyingstar 303 天前 复制了 op 的 case ,尝试把迭代次数开到 10w ,然后编译了 21s 。。。感觉完全不如正常写 constexpr |