版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/tkokof1/article/details/102795683
本文讲述了 C/C 中 自增(自减)运算符 的一些知识~
自增(自减)运算符应该是 C/C 编程中的基础知识了,而自增(自减)运算符又有两种形式,分别是 前置自增(自减) 和 后置自增(自减) (出于简单考虑,后文仅以自增运算符进行举例讲解).
譬如表达式 i 和 i 就分别表示对 i 进行前置自增 和 对 i 进行后置自增,用代码来说明的话, i(前置自增) 大概和以下代码等价:
代码语言:javascript复制i = i 1;
return i;
而 i (后置自增) 则大概和以下代码等价:
代码语言:javascript复制v = i;
i = i 1;
return v;
可以看到, i 和 i 虽然都对 i 进行了增 1 操作,但是 i 返回了自身,而 i 返回了一个临时变量(用以保存 i 之前的数值),所以 i 比 i 要消耗更多的 CPU 资源(因为要使用临时变量),所以自己编码时也慢慢形成了一个习惯: 多用前置自增,少用后置自增.
虽然就现在的程序开发来讲,似乎我们已经不必特别关心 前置自增 和 后置自增 的效率差异了,在合适的情况下,编译器对于 前置自增 和 后置自增 也能生成同样高效的汇编代码了,但是自己依然坚持着上面的编码习惯(多用前置自增,少用后置自增),理由也很简单:虽然 前置自增 和 后置自增 的效率相仿甚至相同,但是 前置自增 仍然在理论上要优于 后置自增.
(下面的示意图是 for 循环中 前置自增 和 后置自增 编译器(MSVC)对应生成的汇编代码,可以看到,两者对应生成的汇编代码是一致的)
但后面从 Game Engine Architecture 中却了解到了一个有些颠覆的知识: 前置自增 效率上其实一般是要 慢于 后置自增 的 !!!
这里的原因是 前置自增 会比 后置自增 产生更多的 指令流水线停顿(stall),一般情况下,虽然 前置自增 对比 后置自增 会产生更少的指令操作,但是其产生的指令流水线停顿对效率的影响更大,所以我们应该: 多用后置自增,少用前置自增 !
自己初看到这个结论时自然是将信将疑,毕竟和自己之前的认知大相径庭,虽然我也认可书中给出的理由解释,但还是决定要亲自测试验证一下:
代码语言:javascript复制// pre-increment profile
int index = 0;
int value = 0;
for (int i = 0; i < count; i)
{
value = index;
}
// post-increment profile
int index = 0;
int value = 0;
for (int i = 0; i < count; i)
{
value = index ;
}
首先确定 前置自增 和 后置自增 确实生成了不同的汇编代码:
后面就是简单的测量运行时间了,结果也确实如书中所说:
后置自增 平均要比 前置自增 快 20% 左右 ~
总结
多用后置自增,少用前置自增,虽然 后置自增 会产生更多的指令操作,但是一般情况下对指令流水线的影响更小,所以相比 前置自增 其实反而更加高效.
从一开始懵懂的编写 后置自增 代码,到后来了解了内部原理后改用 前置自增,再到现在了解了更多原理之后改回 后置自增,颇有些"返璞归真"的味道~