为什么打印模板元编程计算阶乘结果,比打印 for 循环计算阶乘结果更耗时 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
zcion
V2EX    C++

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

  •  
  •   zcion 304 天前 2489 次点击
    这是一个创建于 304 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

    二是 constexpr auto tmp 的值在编译的时候就算好了,不会耗时的,你这个代码主要测量了打印到 cout 和取高精度时间的开销。这些都是 syscall ,响应的耗时是很随机的。
    vvhy
        6
    vvhy  
       304 天前
    不要把 cout 放在计时里,估计占 99%的时间
    ipwx
        7
    ipwx  
       304 天前
    只有我想吐槽,900 个数连乘,肯定已经溢出了么。。。
    zcion
        8
    zcion  
    OP
       304 天前
    @billccn 感谢大佬,认识了个新东西
    zcion
        9
    zcion  
    OP
       304 天前
    @vvhy 这里好奇的点是输出一个编译期计算的结果花费了更多的时间
    zcion
        10
    zcion  
    OP
       304 天前
    @ipwx 溢出了也无所谓,主要是多次循环消耗些时间
    kyingstar
        11
    kyingstar  
       303 天前
    复制了 op 的 case ,尝试把迭代次数开到 10w ,然后编译了 21s 。。。感觉完全不如正常写 constexpr
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     903 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 24ms UTC 19:46 PVG 03:46 LAX 12:46 JFK 15:46
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86