开发是一门技术,编程是一种艺术

游戏老手,编程新手
随笔 - 11, 文章 - 0, 评论 - 58, 引用 - 0
数据加载中……

i++, ++i 和i=i+1究竟哪个快?

i++, ++i 和i=i+1究竟哪个快?

引子

以前学习C++时,被告知i++的速度要比i=+1快,而++i的速度又要比i++快。即在效率上有:++i > i++ > i=i+1。所以如果单单只是需要进行递增时,用++i是最好的。但是某天我突然觉得:这三个运算的目的都是一样,开销按道理应该相同才对。难道编译器如此之笨?就连这点优化都不会做?

运行时间测试(VS2008)

先用c#做了一遍:

static private void Test()

{

int counter = 0;

Stopwatch timer = new Stopwatch();

 

timer.Start();

for (int i = 0; i < 2147483647; i++)

{

counter++;

}

timer.Stop();

Console.WriteLine("i++: " + timer.ElapsedMilliseconds);

 

timer.Reset();

counter=0;

timer.Start();

for (int i = 0; i < 2147483647; ++i)

{

++counter;

}

timer.Stop();

Console.WriteLine("++i: " + timer.ElapsedMilliseconds);

 

timer.Reset();

counter=0;

timer.Start();

for (int i = 0; i < 2147483647; i = i + 1)

{

counter=counter+1;

}

timer.Stop();

Console.WriteLine("i=i+1: "+timer.ElapsedMilliseconds);

 

Console.WriteLine();

 

}

从结果来看,几乎没有分别,每个算式都有机会获得第一名。所以我觉得这3个算式对编译器来说应该是没有分别的。

用c++做了一遍

void test()

{

    int elapTicks;

    double elapMilli;

    clock_t Begin, End;

    int counter = 0;

 

 

    Begin = clock() * CLK_TCK; //start the timer

    for(int i=0; i<2147483647; i++) counter++;

    End = clock() * CLK_TCK; //stop the timer

 

    elapTicks = End - Begin; //the number of ticks from Begin to End

    elapMilli = elapTicks/1000; //milliseconds from Begin to End

    cout<<"i++: "<<elapMilli<<"\n";

 

    counter=0;

    Begin = clock() * CLK_TCK; //start the timer

    for(int i=0; i<2147483647; ++i) ++counter;

    End = clock() * CLK_TCK; //stop the timer

 

    elapTicks = End - Begin; //the number of ticks from Begin to End

    elapMilli = elapTicks/1000; //milliseconds from Begin to End

    cout<<"++i: "<<elapMilli<<"\n";

 

    counter=0;

    Begin = clock() * CLK_TCK; //start the timer

    for(int i=0; i<2147483647; i=i+1)counter=counter+1;

    End = clock() * CLK_TCK; //stop the timer

 

    elapTicks = End - Begin; //the number of ticks from Begin to End

    elapMilli = elapTicks/1000; //milliseconds from Begin to End

    cout<<"i=i+1: "<<elapMilli<<"\n\n";

 

}

结果也是类似。

结论

i++, ++i 和i=i+1的区别,应该只是纯粹理论上的区别(即按照相应的表达算式进行编译)。个人猜测对于以上3个表达式,编译器在编译之后应该生成一样的语句。不过我不懂汇编,也不懂如何进一步深入测试。就此次测试的结果来看,3个表达式的时间开销是一样的(每次运行结果误差应该是其他原因)。当然,此分析仅限于VS2008,有可能这3个语句在其他编译器上的性能会有所不同。

欢迎指正。

posted on 2010-07-14 13:41 54sun 阅读(10323) 评论(29)  编辑 收藏 引用 所属分类: 随笔

评论

# re:   回复  更多评论   

明显++i
2010-07-14 14:00 | wangfan1985@gmail.com

# re:   回复  更多评论   

编译器不会那么笨的,我自己写的那个都能发现他们三个是一样的。
2010-07-14 14:18 | 陈梓瀚(vczh)

# re:   回复  更多评论   

@wangfan1985@gmail.com
那是你用的编译器过于烂,或者你没有打开应有的优化选项,导致的。
2010-07-14 14:19 | 陈梓瀚(vczh)

# re: [未登录]  回复  更多评论   

这么测试是不对的,应该直接看汇编
2010-07-14 14:23 | cyantree

# re: i++, ++i 和i=i+1究竟哪个快?  回复  更多评论   

# re: i++, ++i 和i=i+1究竟哪个快? 回复 更多评论 删除评论
因为C++可以重载操作符。
所以, 除非"知道i的类型", 表达式 i++, ++i, i=i+1 的行为"未知"。

从语意上说,所需要的操作: ++i < i++ (多一个复制), i=i+1(多一个赋值)。
所以: "如果只需要++i, 就不要写成i++; 如果只需要i++, 就不要写成i=i+1"。
养成这种习惯, 无论i是什么类型都无所谓。

另一方面, 在已知i是int的情况下,如果某编译器生成的代码有显著区别, 那可以把它丢了……
2010-07-14 14:16 | OwnWaterloo
2010-07-14 14:34 | 54sun

# re: i++, ++i 和i=i+1究竟哪个快?  回复  更多评论   

对这3种情况分别反汇编以后都得到以下结果
00401004 |. C745 FC 00000>mov dword ptr [ebp-4], 0
0040100B |. EB 09 jmp short 00401016
0040100D |> 8B45 FC /mov eax, dword ptr [ebp-4]
00401010 |. 83C0 01 |add eax, 1
00401013 |. 8945 FC |mov dword ptr [ebp-4], eax
00401016 |> 817D FC E8030> cmp dword ptr [ebp-4], 3E8
0040101D |. 7D 02 |jge short 00401021
0040101F |.^ EB EC \jmp short 0040100D

所以说优化后的效率是一样的
2010-07-14 14:42 | lwch

# re: i++, ++i 和i=i+1究竟哪个快?  回复  更多评论   

搞个 ++ 对象试一试。
区别很大
看看STL的源码就清楚了
2010-07-14 18:27 | haven

# re: i++, ++i 和i=i+1究竟哪个快?  回复  更多评论   

楼主不厚道啊,你把明显能优化成一样的代码放在一起当然编译器没有那么笨,效率肯定是一样的
你试试这样
j = i;
i = i + 1;

i = i + 1;
j = i;

j = i ++

j = ++ i

理论上 j = i ++ 可能会慢一些,看情况,因为要先保存 i 的值,然后计算,然后将旧值赋值给 j ,其他的应该效率理论上应该是相同的
2010-07-14 18:59 | 楼主不厚道

# re: i++, ++i 和i=i+1究竟哪个快?[未登录]  回复  更多评论   

纯primitive type区别并不是太大, 用object和重载的运算符就能看出区别来了.
2010-07-14 22:59 | R

# re: i++, ++i 和i=i+1究竟哪个快?[未登录]  回复  更多评论   

应该看看生成的汇编代码
2010-07-15 00:51 | frank

# re: i++, ++i 和i=i+1究竟哪个快?  回复  更多评论   

对于内置类型,差别不大。对于自定义类型(class type)差别就出来啦。
自己写个copy操作比较费时的class试试看,比如class有20000个元素的string数组,这样后置操作符的需要多做一份拷贝的cost就出来了。
2010-07-15 00:58 | kirby

# re: i++, ++i 和i=i+1究竟哪个快?  回复  更多评论   

不知道 如果是对象调用的话 差别大不大。
编译器会不会优化。
2010-07-15 01:13 | 欣萌

# re: i++, ++i 和i=i+1究竟哪个快?  回复  更多评论   

不懂汇编?

那么请你用更加精确的计时手段!
2010-07-15 01:31 | coolypf

# re: i++, ++i 和i=i+1究竟哪个快?  回复  更多评论   

其实,编译器一般都会把i++,++i,i=i+1,优化成汇编,inc eax,,
至于你的测试,我估计是在程序运行过程中,系统的调度所产生的偏差(因为某段代码用完了他的时间片,系统切换其它工作线程),或其它未知原因:),
因为他们所对应的指令是一样的.
2010-07-15 01:36 | luoqi

# re: i++, ++i 和i=i+1究竟哪个快?  回复  更多评论   

。。。无语,有缺陷的测试得到的必然是有缺陷的结果,包括你代码得到的反汇编
2010-07-15 02:11 | 。。。

# re: i++, ++i 和i=i+1究竟哪个快?  回复  更多评论   

# re: i++, ++i 和i=i+1究竟哪个快? 回复 更多评论
其实,编译器一般都会把i++,++i,i=i+1,优化成汇编,inc eax,,
至于你的测试,我估计是在程序运行过程中,系统的调度所产生的偏差(因为某段代码用完了他的时间片,系统切换其它工作线程),或其它未知原因:),
因为他们所对应的指令是一样的.


那么,你认为
j = ++ i
怎么优化成汇编,inc eax,呢?
2010-07-15 02:14 | 。。。

# re: i++, ++i 和i=i+1究竟哪个快?  回复  更多评论   

# re: i++, ++i 和i=i+1究竟哪个快? 回复 更多评论
# re: i++, ++i 和i=i+1究竟哪个快? 回复 更多评论 删除评论
因为C++可以重载操作符。
所以, 除非"知道i的类型", 表达式 i++, ++i, i=i+1 的行为"未知"。

从语意上说,所需要的操作: ++i < i++ (多一个复制), i=i+1(多一个赋值)。
所以: "如果只需要++i, 就不要写成i++; 如果只需要i++, 就不要写成i=i+1"。
养成这种习惯, 无论i是什么类型都无所谓。

另一方面, 在已知i是int的情况下,如果某编译器生成的代码有显著区别, 那可以把它丢了……
2010-07-14 14:16 | OwnWaterloo
2010-07-14 14:34 | 54sun


这是正解。
2010-07-15 02:20 | 老安

# re: i++, ++i 和i=i+1究竟哪个快?  回复  更多评论   

@haven
对象的两个++根本就是不同的函数,没有可比性。
2010-07-15 02:53 | 陈梓瀚(vczh)

# re: i++, ++i 和i=i+1究竟哪个快?  回复  更多评论   

@楼主不厚道
对了,我假设 i 是基本类型,
暂时先不考虑 i 是非基本类型的情况,因为那种情况 i++; ++i; i + 1; 肯定根据自己的定义了,所以显然的问题暂不考虑
2010-07-15 02:58 | 楼主不厚道

# re: i++, ++i 和i=i+1究竟哪个快?  回复  更多评论   

各有各的用处~~
2010-07-15 03:21 | 日光博客

# re: i++, ++i 和i=i+1究竟哪个快?  回复  更多评论   

你这个测试,估计大部分的测试时间被for占去了吧
2010-07-15 08:09 | freebug

# re: i++, ++i 和i=i+1究竟哪个快?  回复  更多评论   

i是int下,都一样。


这个之所以分这么细,完全是C++里重载 operator ++ 的缘故,自定义类型的这两个操作,差别挺大的,i++ 需要比++i额外产生一个新对象,并返回该新对象。
2010-07-15 09:23 | www

# re: i++, ++i 和i=i+1究竟哪个快?  回复  更多评论   

这么多回复,已经很明了了,

j = ++ i

这句就可以看出来,++i和i++效率不同,
++i直接把i改变,然后值给j就可以
i++要先保存i的值,然后把i改变,再把i给j

还有什么好争的

如果有人非说编译器优化,i++可以先把值给j,然后改变i,跟++i一样,
那就再考虑 j= j + i++ 吧


回复别人问题麻烦先仔细看上面其他人回复的内容,不要想当然
2010-07-15 12:00 | 。。。

# re: i++, ++i 和i=i+1究竟哪个快?  回复  更多评论   

@楼主不厚道
其实做这个测试的目的仅仅是想知道在只需要把i递增1的情况下,这三个算式是否有区别。比如在for语句for (int i = 0; i < 10; i=i+1),如果是c,那么i=i+1是个常用的,到了c++一般都用i++。就个人来说,我觉得i++这个形式就反映了其语言本身的名字,所以看起来很合适。但有人认为++i更效率,于是写成for (int i = 0; i < 10;++i)。并不是说这样有问题,只是我个人认为i++比++i要看起来自然些。更何况,其实这里++i并没有比i++快。

此文并不是要讨论运算符重载。

@OwnWaterloo
所以: "如果只需要++i, 就不要写成i++; 如果只需要i++, 就不要写成i=i+1"。
养成这种习惯, 无论i是什么类型都无所谓。

我想说的是,如果只是一个int递增,我会用i++。形式上比较美,而且效率和++i一样。特别是在for语句里面。(当然,你对3者区别的解释是十分简单明了正确的)
2010-07-15 20:54 | 54sun

# re: i++, ++i 和i=i+1究竟哪个快?  回复  更多评论   

不要用编译器内置的数据类型测试,这样测试看不出什么的,表明上一样,其实是有区别的。换成自定义的数据类型,这样的测试结果更客观些。
2010-07-20 00:22 | Benjamin

# re: i++, ++i 和i=i+1究竟哪个快?  回复  更多评论   


++i 中的 ++ 不一定是 i 的成员函数, 嗯
2010-07-21 17:15 | Soli

# re: i++, ++i 和i=i+1究竟哪个快?  回复  更多评论   

理论上来说, ++i可能会更快, 只要一条汇编指令inc就可以了. 当然i++同样也是.
当然这个也跟编译器优化有关, 且也难保证都会优化.
2010-07-25 01:14 | programmer huang

# re: i++, ++i 和i=i+1究竟哪个快?  回复  更多评论   

貌似能证明c++速度快。。。
其实最好的办法是看底层代码,c#看IL,c++看汇编(用ida吧)。
同种语言对比,如果编译的一样,那就说明没区别。(也许汇编不同,但实质一样)

我觉得i++和++i的效率区别应该在是否对左值赋值的问题上,
int a=i++;

int a=++i;
不过只是猜测,一般我喜欢i++,看起来好看。不太喜欢写复杂的表达式,宁愿分开写,多加括号。
2011-02-14 17:19 | idreamer

# re: i++, ++i 和i=i+1究竟哪个快?  回复  更多评论   

你这测试不对
2011-04-27 08:20 | KA ZHA FEI

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理