5

我有以下两个循环:

#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;
}

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

4

8 回答 8

10

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

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

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

于 2010-08-02T07:18:36.903 回答
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++ 比 ++i 效率低,如何显示这个?

于 2010-08-02T07:23:23.370 回答
4

这些循环等效于 int 类型的归纳变量。后增量与前增量问题已在此处多次回答。试着搜索一下档案。

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

于 2010-08-02T07:17:00.947 回答
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:

你能找到任何不同之处吗?:) (除了 i 和 j 位于堆栈 -8(%ebp) 和 -12(%ebp) 上的不同位置)

于 2010-08-02T07:49:58.870 回答
1

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

于 2010-08-02T07:24:45.893 回答
1

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

知道你的 CPU 在你把它放在这里的负载之外做什么。

它可能是启动计划任务、处理中断,以及各种会影响结果的事情。

您可能需要运行一百万次,剔除异常值,然后对其余的取平均值以获得一个不错的样本。

最重要的是,一百次迭代并不多,尤其是因为您正在执行函数调用,cout这很可能会占用执行循环控制所花费的时间。

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

于 2010-08-02T07:14:29.610 回答
1

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

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

于 2010-08-02T07:17:00.483 回答
-1

++i 是前增量
i++ 是后增量

详情见运营

于 2010-08-02T07:22:06.320 回答