g++的思考

這幾天在看數據庫原理時,提到了查詢優化,不禁聯想到編譯器的優化是怎樣的?

就比如在彙編中,一般a/2可以通過邏輯右移就得出結果的,這算是優化,不用經過算術指令老老實實地除。
在這情況下,(a+b)/2明顯比a/2+b/2更快,那後者在編譯器會優化爲前者嗎?這就是mark的原因。


反彙編

先來看a/2+b/2,反彙編後,去掉相關的棧操作(子程序eip進出棧),主要顯示

0x08048583 <+6>: movl $0x3e8,-0xc(%ebp)
0x0804858a <+13>: movl $0x5dc,-0x8(%ebp)
0x08048591 <+20>: mov -0xc(%ebp),%eax
0x08048594 <+23>: mov %eax,%edx
0x08048596 <+25>: shr $0x1f,%edx
0x08048599 <+28>: add %edx,%eax
0x0804859b <+30>: sar %eax
0x0804859d <+32>: mov %eax,%edx
0x0804859f <+34>: mov -0x8(%ebp),%eax
0x080485a2 <+37>: mov %eax,%ecx
0x080485a4 <+39>: shr $0x1f,%ecx
0x080485a7 <+42>: add %ecx,%eax
0x080485a9 <+44>: sar %eax
0x080485ab <+46>: add %edx,%eax

(a+b)/2,反彙編後,主要顯示

0x08048583 <+6>: movl $0x3e8,-0xc(\%ebp)
0x0804858a <+13>: movl $0x5dc,-0x8(%ebp)
0x08048591 <+20>: mov -0x8(%ebp),%eax
0x08048594 <+23>: mov -0xc(%ebp),%edx
0x08048597 <+26>: add %edx,%eax
0x08048599 <+28>: mov %eax,%edx
0x0804859b <+30>: shr $0x1f,%edx
0x0804859e <+33>: add %edx,%eax
0x080485a0 <+35>: sar %eax

差不多的原理,先將局部變量入棧,然後從棧中取參進行操作,但很明顯看出兩者的不同,前者多了一次 /2 操作。

0x080485a2 <+37>: mov %eax,%ecx
0x080485a4 <+39>: shr $0x1f,%ecx
0x080485a7 <+42>: add %ecx,%eax
0x080485a9 <+44>: sar %eax

可見編譯器並沒有優化這類語句。可能因爲兩者不同。
(a+b)/2的確節省一次操作,節省了時間,但比a/2+b/2溢出的可能性要大,主要取決於a和b的值了,非編譯器之功了。

同樣a、b交換,可以

t=a;
a=b;
b=t;

也可以

a=b^b;
b=a^b;
a=a^b;

實際就是mov和xor的差異
t=a;的彙編代碼

0x080485c1 <+20>: mov -0xc(%ebp),%eax
0x080485c4 <+23>: mov %eax,-0x4(%ebp)


b=a^b;的彙編代碼

0x080485d9 <+44>: mov -0xc(%ebp),%eax
0x080485dc <+47>: xor %eax,-0x8(%ebp)

編譯器可以轉化循環、條件語句和遞歸函數、消除整塊代碼和利用目標指令集的優勢讓代碼變得高效而簡潔。

這纔是編譯器能做的。

mark,進一步瞭解可以參考:
有關整數除法的優化
關於VS編譯器的優化例子(裏面的例子在g++試過,彙編是不一樣的)
關於軟件源代碼編譯(就是make install那一套)

發佈了49 篇原創文章 · 獲贊 16 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章