V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
zcion
V2EX  ›  C++

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

  •  
  •   zcion · 16 天前 · 1026 次点击

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

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

    二是 constexpr auto tmp 的值在编译的时候就算好了,不会耗时的,你这个代码主要测量了打印到 cout 和取高精度时间的开销。这些都是 syscall ,响应的耗时是很随机的。
    vvhy
        6
    vvhy  
       16 天前
    不要把 cout 放在计时里,估计占 99%的时间
    ipwx
        7
    ipwx  
       16 天前
    只有我想吐槽,900 个数连乘,肯定已经溢出了么。。。
    zcion
        8
    zcion  
    OP
       16 天前
    @billccn 感谢大佬,认识了个新东西
    zcion
        9
    zcion  
    OP
       16 天前
    @vvhy 这里好奇的点是输出一个编译期计算的结果花费了更多的时间
    zcion
        10
    zcion  
    OP
       16 天前
    @ipwx 溢出了也无所谓,主要是多次循环消耗些时间
    kyingstar
        11
    kyingstar  
       15 天前
    复制了 op 的 case ,尝试把迭代次数开到 10w ,然后编译了 21s 。。。感觉完全不如正常写 constexpr
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1512 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 22ms · UTC 17:06 · PVG 01:06 · LAX 09:06 · JFK 12:06
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.