【问题标题】:Creating object inside/outside loop: allocation and speed在循环内/外创建对象:分配和速度
【发布时间】:2014-03-12 22:52:56
【问题描述】:

我正在对我的GaussQuadrature 模板类的一部分进行基准测试,方法是在循环中调用成员函数Quadrature 1M 次并对整个循环进行计时(如果您知道更好的方法,请告诉我!)。在这个类的预期应用中,构造函数将被调用一次,而例程将被调用多次,但我尝试在循环内部调用构造函数 1M 次,在外部调用一次只是为了看看哪个更快。

  1. 如果我在循环内调用构造函数,则每次迭代都会调用构造函数,但看起来它被分配到相同的内存空间(注意下面的 sn-p,构造函数内部有语句 std::cout << "ctor\n";) .这是巧合,还是更深层的原因?这是sn-p:

    for(int i = 0; i < 1000000; ++i) {
      GaussQuadrature<double> Q(N, func, a, b);  // some arguments
      Q.Quadrature();           
    
      std::cout << "address: " << &Q << "\n";                                    
    }
    

    这给出了:

    ctor
    address: 0x7fff23059400
    ctor
    address: 0x7fff23059400
    .
    .
    .
    ctor
    address: 0x7fff23059400
    

在没有循环或构造函数中的打印语句的情况下计时,即

    clock_t tic = clock();    

    for(int i = 0; i < 1000000; ++i) {
      GaussQuadrature<double> Q(N, func, a, b);
      Q.Quadrature();                                           
    }

    float elapsed = (clock() - (float)tic) / CLOCKS_PER_SEC;

    std::cout << std::setprecision(10) << "time: " << elapsed << "\n";

结果

    time: 12.47999954
  1. 现在,如果我改为在循环外调用构造函数,那么它当然只调用一次,但实际上需要更长的时间:

    clock_t tic = clock();  
    GaussQuadrature<double> Q(N, func, a, b);  
    
    for(int i = 0; i < 1000000; ++i) {
      Q.Quadrature();                                   
    }
    
    float elapsed = (clock() - (float)tic) / CLOCKS_PER_SEC;
    
    std::cout << std::setprecision(10) << "time: " << elapsed << "\n";
    

    这给了

    time: 12.65999985
    

我运行了几次,结果相似。有什么想法吗?谢谢!

【问题讨论】:

  • 现在的编译器非常聪明。使用循环内的构造函数,不会每次都创建对象,因为编译器知道它是相同的。而且它非常方便,因为这样你可以专注于干净的代码、作用域……如果你的对象 Q 没有在循环外使用,建议在循环内声明它。

标签: c++ performance class


【解决方案1】:

首先,在进行计时测试时,您需要关闭任何文本输出,例如 printfs 和 couts。这很重要,因为通过包含它们,您会隐藏班级的真实表现。你的测试方法没问题。调用算法 100 万次是合理的。但请记住高速缓存。根据内存管理器的行为,接收相同的内存指针是可能的。最后,我猜,在你注释掉文本输出之后,你的第二个实现会比第一个更快。

最终建议:您可能更喜欢使用 QueryPerformanceFrequency 和 QueryPerformanceCounter 函数用于更精确的计时。

【讨论】:

  • 他得到了相同的指针,因为它是在堆栈上分配的。每次通过循环时,它都会被分配和释放,并且每次都在堆栈上释放相同的插槽。
  • 是的,我同意你的观点,我不想让他更加困惑:)
  • @SemihOzmen 实际上我在没有打印语句的情况下进行了计时;请看我更新的帖子。关于为什么它被调用 1M 次而不是只调用一次会更快的任何想法?
  • 正如我之前所说,内存缓存可能会导致这种行为..为了获得有关编译器和 CPU 硬件设置的编译器优化的更多详细信息,并且正如我之前建议的那样,尝试使用更准确的计时功能。
【解决方案2】:

这可能是编译器优化的结果。 for 循环可以自动“展开”http://en.wikipedia.org/wiki/Loop_unwinding,最大限度地减少分支,并且这种展开还可以通过 CPU 的内部并行性和流水线来使用。

“如果循环中的语句相互独立(即循环中较早出现的语句不影响其后的语句),则这些语句可能会并行执行。”

尝试禁用所有优化并再次检查这些时间。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-12-08
    • 2014-03-31
    • 2020-08-14
    • 1970-01-01
    • 2020-08-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多