程式設計師一般都喜歡談論程式設計技巧,尤其是能優化程式效率的技巧,c++程式設計師都不例外。但很多廣泛流傳的技巧都是基於乙個假設,就是編譯器非常愚蠢。但事實上剛好相反,現在的編譯器其實非常聰明。不信的話你看下面的例子。
test函式的作用是返回從1加到100的值,大家都知道結果是5050。使用gcc編譯器將其編譯成彙編:
gcc -o2 -s test.c
vi test.s
生成的彙編:
test:
pushl %ebp
movl $5050, %eax
movl %esp, %ebp
popl %ebp
ret編譯器也已經知道答案5050,所以當你呼叫test函式時直接返回5050而不需計算。怎麼樣,編譯器還不笨吧?
下面看看幾個常見的c++語句的編譯優化。為簡單起見,以下討論都基於gcc優化編譯。
有些人說不要用i++而要用++i,因為i++要先儲存原來的值會比較慢(據說有些書是這麼說的)。但事實上作為一條語句使用時(而不是作為表示式嵌入到語句中),無論i++、++i、i+=1或i=i+1編譯後都只使用一條指令(inc或add),效果是一樣的。當然作為表示式嵌入到語句中時會不一樣,但這時候i++和++i一般都不能混用。
c++**:
彙編**:
movl 8(%ebp), %ebx
movl 12(%ebp), %ecx
movl 16(%ebp), %edx
movl 20(%ebp), %eax
addl $1, (%ebx)
addl $1, (%ecx)
addl $1, (%edx)
addl $1, (%eax)
函式區域性變數使用的棧空間是在進入函式時一次分配的,而不是在宣告時分配,因此在迴圈裡宣告變數並不會導致效能下降。
彙編**:
movl $10, %ebx
subl $272, %esp #分配272位元組棧空間
leal -264(%ebp), %esi #取buf位址
.l2:
movl %esi, (%esp) #buf位址入棧
call test2 #呼叫test2
subl $1, %ebx
jne .l2 #迴圈未結束則跳到l2
a*2被編譯成a+a;無符號數a/2被編譯成a>>1;有符號數a/2沒看懂,但不是除運算。
memset函式常用來初始化大段記憶體,但對小資料來說memset能否保持足夠高效呢?
看這段程式:
編譯成彙編:
movl $0, -24(%ebp) #設定s1
movl $0, -20(%ebp)
movl $0, -16(%ebp)
movl $0, -12(%ebp)
call test2 #呼叫test2
leal -8216(%ebp), %edx #設定s2
xorl %eax, %eax
movl %edx, %edi
movl $2048, %ecx
rep stosl
movl %edx, (%esp) #呼叫test2
call test2
movl %ebx, (%esp) #設定s3
movl $8193, 8(%esp)
movl $0, 4(%esp)
call memset
movl %ebx, (%esp) #呼叫test2
call test2
當資料長度比較小時(如s1是16位元組),memset被編譯成連續的賦值語句;當資料長度不大於8kb時(如s2),memset用串操作指令來實現;當資料長度大於8kb時(如s3),memset被編譯成函式呼叫。
EnumSet的幾個例子
enumset 是乙個與列舉型別一起使用的專用 set 實現。列舉set中所有元素都必須來自單個列舉型別 即必須是同型別,且該型別是enum的子類 列舉型別在建立 set 時顯式或隱式地指定。列舉 set 在內部表示為位向量。此表示形式非常緊湊且高效。此類的空間和時間效能應該很好,足以用作傳統上基於...
儲存過程幾個例子
create or replace procedure peace if is cursor var c is select from grade begin for temp in var c loop if temp.course name os then dbms output.put lin...
java註解的幾個例子
註解的生命階段 1.原始檔 source 2.class檔案 class 3.記憶體中 runtime retentionpolicy 是乙個列舉 一共就這三個值,用來表示註解的生命階段 override retentionpolicy.source suppresswarnings retenti...