关于循环速度的问题

时间:2010-08-02 07:08:50

标签: c++ micro-optimization

我有以下两个循环:

#include <iostream>
#include <stdlib.h>
#include <time.h>

using namespace std;
int main(){

    int start=clock();
    for (int i=0;i<100;i++)
        cout<<i<<" "<<endl;
    cout<<clock()-start<<"\n"<<endl;
    cout<<"\n";

    int start1=clock();
    for (int j=0;j<100;++j)
        cout<<j<<" "<<endl;
    cout<<"\n";
    cout<<clock()-start1<<" \n "<<endl;

    return 0;
}

我跑了三次。在前两次运行中,第二次循环最快,但在第三次运行时,第一次循环最快。这是什么意思?哪个更好?这取决于具体情况吗?

8 个答案:

答案 0 :(得分:10)

循环的运行时间绝大部分由输入输出操作决定。这意味着您观察到的时间1)与循环的实际性能无关(即i++ vs ++j),2)几乎是不可预测和不稳定的(基本上是随机的)。

换句话说,你的实验毫无意义。它绝对没有意义。

最后,在不使用内置++运算符的结果的情况下,后缀和前缀增量之间绝对没有区别。在任何合理的编译器中,两个循环都将具有完全相同的性能。

答案 1 :(得分:5)

在您的情况下,它可能是标准测量误差,您使用后增量或预增量无关紧要。对于标准类型(int,byte ...),这没关系。

您应该习惯使用预增量,因为如果您在类上使用它们会对性能产生影响,具体取决于这些运算符的实现方式。后增量运算符i ++必须复制对象

例如:

class integer
{
public:
  integer(int t_value)
    : m_value(t_value)
  {
  }

  int get_value() { return m_value; }

  integer &operator++() // pre increment operator
  {
    ++m_value;
    return *this;
  }

  integer operator++(int) // post increment operator
  {
    integer old = *this; // a copy is made, it's cheap, but it's still a copy
    ++m_value;
    return old; // Return old copy
  }

private:
  int m_value;

看看以下答案

StackOverflow: i++ less efficient than ++i, how to show this?

答案 2 :(得分:4)

这些循环对于int类型的归纳变量是等效的。这里已经多次回答了后增量与预增量问题。尝试稍微搜索一下档案。

此外,与标准IO相比,增量操作只需要一小部分时间。您的测试是测量IO的速度而不是增量操作的速度。

答案 3 :(得分:3)

旧GCC 3.4.4执行此操作:

第一次循环:

.L11:
        cmpl    $99, -8(%ebp)
        jg      .L12
        subl    $8, %esp
        pushl   $_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
        subl    $12, %esp
        pushl   $.LC0
        subl    $12, %esp
        pushl   -8(%ebp)
        pushl   $_ZSt4cout
.LCFI7:
        call    _ZNSolsEi
        addl    $20, %esp
        pushl   %eax
        call    _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
        addl    $20, %esp
        pushl   %eax
.LCFI8:
        call    _ZNSolsEPFRSoS_E
        addl    $16, %esp
        leal    -8(%ebp), %eax
        incl    (%eax)
        jmp     .L11
.L12:

第二次循环:

.L14:
        cmpl    $99, -12(%ebp)
        jg      .L15
        subl    $8, %esp
        pushl   $_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
        subl    $12, %esp
        pushl   $.LC0
        subl    $12, %esp
        pushl   -12(%ebp)
        pushl   $_ZSt4cout
.LCFI13:
        call    _ZNSolsEi
        addl    $20, %esp
        pushl   %eax
        call    _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
        addl    $20, %esp
        pushl   %eax
.LCFI14:
        call    _ZNSolsEPFRSoS_E
        addl    $16, %esp
        leal    -12(%ebp), %eax
        incl    (%eax)
        jmp     .L14
.L15:

你能找到任何差异吗? :)(除了我和j在堆栈上的不同位置-8(%ebp)和-12(%ebp))

答案 4 :(得分:1)

这意味着您应该使用统计技术来确定哪个循环更快(我希望这些技术确定它们彼此相当不同)。

没有知道你的CPU正在做什么,而不是你把它放在这里的负载。

它可能会启动计划任务,处理中断,各种会扭曲结果的事情。

您可能需要进行一百万次运行,抛弃异常值,并对其余值进行平均以得到合适的样本。

最重要的是,一百次迭代并不多,特别是因为你正在对cout进行函数调用,这可能沼泽进行循环控制所花费的时间。

当我在UNIX下运行检查时,由于这个原因,我使用已用时间。无论经过的时间如何,系统和用户时间都会给出给定进程使用CPU的秒数。

答案 5 :(得分:1)

++我应该得到与i ++相同的机器代码,并且在任何中途正常的编译器上使用未使用的结果(前提是它没有以一种奇特的方式过载,这不是int的情况)。即使它没有(你需要从上个世纪挖出一个非常愚蠢的编译器),差异是如此之小,你永远不必担心它。好吧,有些情况下你确实需要从中挤出一小部分性能,但是我们每个人都会遇到这种情况。即使这样,循环体也有更多的优化潜力(即使在你过于简单的例子中 - I / O比复制机器字更加省钱)。

或者用一句话说:过早优化是万恶之源。

答案 6 :(得分:1)

从我看到的,循环中唯一的区别是循环变量的前/后增量。大部分处理时间将花在cout上。相比之下,增量的时间可以忽略不计。

答案 7 :(得分:-1)

++我是preincrement
i ++是postincrement

有关详细信息,请参阅Operator