编译器能否优化后置自增操作符

 如果你要让某个变量的值加1,可以用 i++;  也可以用++i;

通常,编码规范都建议我们如果考虑性能,这种情况下最好用前置自增,因为后置自增会多一无用的复制操作。

但这种做法往往跟我们的习惯不相符,我自己还是看“i++”顺眼一点儿。

 

也许,编译器没那么傻,它们可以根据使用环境优化后置自增,真是这样的话,我们就不必破坏自己的习惯了?

试验一下吧。原始类型的自增操作肯定是可以能被编译器优化的。那么自己写的类的后置自增操作符能否被优化呢?

 

我在VC2008(vc9)里写了下面的代码:

class t_iter
{
public:
 t_iter(void):m_n(0){}
 ~t_iter(void){}
public:
 t_iter & operator ++ (){
  m_n++;
  return *this;
 }
 t_iter operator ++ (int){
  t_iter tempIter = *this;
  m_n++;
  return tempIter;
 }
 int GetValue() const {
  return m_n;
 }
private:
 int m_n;
};

int _tmain(int argc, _TCHAR* argv[])
{
 t_iter iter; 
 iter++;
 return iter.GetValue();
}


代码里面11~15行的“t_iter operator ++ (int)”这个函数就是后置自增操了。t_iter & operator ++ ()是前置操作。

我选了Maximize Speed (/O2)优化选项,也开着“Use Link Time Code Generation (/ltcg)”(其实不开这个也可以的,因为我把t_iter的代码和调用它的main函数写在同一个.cpp文件里了。

打开汇编文件输出选项,并选择Assembly With Source Code (/FAs),build整个程序,就可以看到我们期待的答案了。

编译后产生的汇编文件中main函数的部分如下:

PUBLIC _wmain
; Function compile flags: /Ogtpy
; COMDAT _wmain
_TEXT SEGMENT
_iter$ = -8      ; size = 4
$T12223 = -4      ; size = 4
_argc$ = 8      ; size = 4
_argv$ = 12      ; size = 4
_wmain PROC      ; COMDAT

; 40   : {

 sub esp, 8

; 41   : 
; 42   :  t_iter iter; 
; 44   :  iter++;

 lea eax, DWORD PTR $T12223[esp+8] ;准备存放后置自增函数的返回值
 lea edx, DWORD PTR _iter$[esp+8] 
 mov DWORD PTR _iter$[esp+8], 0
 call ??Et_iter@@QAE?AV0@H@Z  ; t_iter::operator++ 调用后置自增函数

; 48   :  return iter.GetValue();

 mov eax, DWORD PTR _iter$[esp+8]
 add esp, 8
 ret 0
_wmain ENDP
_TEXT ENDS
END

看第22行,编译器并没有把t_iter的后置自增函数内嵌到main函数中,而是在main函数中调用了后置自增函数,那么就不可能对这个进行优化了。


汇编文件中后置自增函数的部分如下:

;	COMDAT ??Et_iter@@QAE?AV0@H@Z
_TEXT	SEGMENT
??Et_iter@@QAE?AV0@H@Z PROC				; t_iter::operator++, COMDAT
; _this$ = edx
; ___$ReturnUdt$ = eax

; 20   : 
; 21   : 		t_iter tempIter = *this;

	mov	ecx, DWORD PTR [edx]
	mov	DWORD PTR [eax], ecx			;准备好返回值,这条指令显然在单纯的自增调用中是无用的

; 22   : 		m_n++;

	inc	ecx
	mov	DWORD PTR [edx], ecx

; 23   : 		return tempIter;
; 24   : 	}

	ret	0
??Et_iter@@QAE?AV0@H@Z ENDP				; t_iter::operator++
_TEXT	ENDS



我感到奇怪的是,编译器并没有把这个后置自增函数内嵌到调用函数中。我还试了在函数声明前加上__inline和 __forceinline,编译器依然无动于衷,始终不真的inline这个函数。我把operator ++ (int)改成一个普通函数,仍然无济于事。

无奈,我们使用自增函数的时候还是应该养成使用前置自增的习惯。当然,如果我们始终能分清我们要自增的变量是原始类型变量还是对象,那么我们可以选择在简单变量上按照顺眼的标准继续使用 i++,而在对象上使用 ++iterator;

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页