为什么打印模板元编程计算阶乘结果,比打印 for 循环计算阶乘结果更耗时

109 天前
zcion  zcion

最近看了 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

1748 次点击
所在节点   C++  C++
11 条回复
zcion
zcion
109 天前
for 循环打印的结果写错了,不过不影响想要表达的意思
bfjm
bfjm
109 天前
调换了一下顺序,前大后小, 应该是程序冷启动的偏差
bfjm
bfjm
109 天前
可以重复测试取中位数 会更精确一点
zcion
zcion
109 天前
@bfjm 感谢大佬,重复 100 次取中位值就正常了
billccn
109 天前
一是你这样做 micro benchmark 完全不准确,因为程序运行会受到操作系统和 CPU 频率等诸多影响,需要使用 google bench 等框架测量才有意义。

二是 constexpr auto tmp 的值在编译的时候就算好了,不会耗时的,你这个代码主要测量了打印到 cout 和取高精度时间的开销。这些都是 syscall ,响应的耗时是很随机的。
vvhy
109 天前
不要把 cout 放在计时里,估计占 99%的时间
ipwx
109 天前
只有我想吐槽,900 个数连乘,肯定已经溢出了么。。。
zcion
109 天前
@billccn 感谢大佬,认识了个新东西
zcion
109 天前
@vvhy 这里好奇的点是输出一个编译期计算的结果花费了更多的时间
zcion
109 天前
@ipwx 溢出了也无所谓,主要是多次循环消耗些时间
kyingstar
108 天前
复制了 op 的 case ,尝试把迭代次数开到 10w ,然后编译了 21s 。。。感觉完全不如正常写 constexpr

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1096242

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX