1. 程式人生 > >C++巨集,普通函式,行內函數的執行速度以及三者的差異

C++巨集,普通函式,行內函數的執行速度以及三者的差異

#include <boost/timer.hpp>


#define  _SUM(x,y) x+y
using std::cout;
using std::endl;
using boost::timer;

const int MAX_ARR_SIZE = 50000;

int sum1(int a,int b)
{
    return a + b;
}

inline int sum2(int a,int b)
{
    return a + b;
}

int main(void)
{
    long iarray1[MAX_ARR_SIZE];
    long iarray2[MAX_ARR_SIZE];

    timer t1;

    cout<<t1.elapsed()<<endl;
    for (size_t star1(0); star1 < MAX_ARR_SIZE;++star1)
    {
        iarray1[star1] = star1;
    }

    for (size_t star1(0); star1 < MAX_ARR_SIZE; ++star1)
    {
        iarray2[star1] = star1;
    }
    double tt1 = t1.elapsed();
    cout << t1.elapsed()<<endl;


    for (size_t star1(0); star1 < MAX_ARR_SIZE; ++star1)
    {
        long result = sum1(iarray1[star1], iarray2[star1]);
        if (star1 == MAX_ARR_SIZE-1)
        {
            cout << "非內聯:" << iarray1[star1] << " + " << iarray2[star1]<<" = " << result << endl;

        }
    }
    cout << "執行非內聯的sum1的時間為:" << t1.elapsed() - tt1<<endl;

    double tt2 = t1.elapsed();

    for (size_t star1(0); star1 < MAX_ARR_SIZE; ++star1)
    {
        long result = sum2(iarray1[star1], iarray2[star1]);
        if (star1 == MAX_ARR_SIZE - 1)
        {
            cout << "內聯:" << iarray1[star1] << " + " << iarray2[star1] << " = " << result << endl;
        }
    }
    cout << "執行內聯的sum2的時間為:" << t1.elapsed() - tt2 << endl;

    double tt3 = t1.elapsed();

    for (size_t star1(0); star1 < MAX_ARR_SIZE; ++star1)
    {
        long result = _SUM(iarray1[star1], iarray2[star1]);
        if (star1 == MAX_ARR_SIZE - 1)
        {
            cout << "巨集:" << iarray1[star1] << " + " << iarray2[star1] << " = " << result << endl;
        }
    }
    cout << "執行巨集的_SUM的時間為:" << t1.elapsed() - tt3 << endl;

    return 0;
}

執行結果為下:

0
0.047
非內聯:49999 + 49999 = 99998
執行非內聯的sum1的時間為:0.014
內聯:49999 + 49999 = 99998
執行內聯的sum2的時間為:0.006
巨集:49999 + 49999 = 99998
執行巨集的_SUM的時間為:0.004

老爺機,效能比較差,但結果比較明顯,巨集>行內函數>普通函式

原因下面分析:

寫個最簡單的測試程式:

test1.c    這個測試下巨集

#include<stdio.h>

#define _SUM(x,y) x+y

void main()
{
   printf("%d\n",_SUM(5,6));
}

對test1.c執行預編譯 :

#gcc -E test1.c -o test1.i

得到下面結果  test1.i

...以上省略啊(包含的標頭檔案加進來)

# 2 "test1.c" 2



void main()
{
   printf("%d\n",5 +6);
}

由此可見,在預編譯時期,巨集被展開了

再將預編譯後的檔案test1.i 進行編譯

gcc –S test1.i –o test1.s

得到以下結果: test1.S

    .file    "test1.c"
    .section    .rodata
.LC0:
    .string    "%d\n"
    .text
.globl main
    .type    main, @function
main:
    pushl    %ebp
    movl    %esp, %ebp
    andl    $-16, %esp
    subl    $16, %esp
    movl    $.LC0, %eax
    movl    $11, 4(%esp)       #在這裡我們的表示式在編譯時期計算出來,並轉換為了立即數
    movl    %eax, (%esp)
    call    printf
    leave
    ret
    .size    main, .-main
    .ident    "GCC: (Ubuntu 4.4.3-4ubuntu5) 4.4.3"
    .section    .note.GNU-stack,"",@progbits

巨集表示式被轉換成了立即數

下面是測試程式碼2:普通函式的

test2.c

#include<stdio.h>

int sum(int x,int y)
{
   return x+y;
}

void main()
{
    printf("%d\n",sum(5,6));
}

同樣將其預編譯:

gcc -E test2.c -o test2.i

得到下面程式碼:

....

# 2 "test2.c" 2

int sum(int x,int y)
{
   return x+y;
}

void main()
{
    printf("%d\n",sum(5,6));
}

再將其編譯:

gcc -S test2.i -o test2.S

得到下面程式碼:

    .file    "test2.c"
    .text
.globl sum
    .type    sum, @function
sum:
    pushl    %ebp
    movl    %esp, %ebp
    movl    12(%ebp), %eax
    movl    8(%ebp), %edx
    leal    (%edx,%eax), %eax
    popl    %ebp
    ret
    .size    sum, .-sum
    .section    .rodata
.LC0:
    .string    "%d\n"
    .text
.globl main
    .type    main, @function
main:
    pushl    %ebp
    movl    %esp, %ebp
    andl    $-16, %esp
    subl    $16, %esp
    movl    $6, 4(%esp)
    movl    $5, (%esp)
    call    sum                             #在這個地方產生了函式呼叫,也是使程式執行速度下降的原因
    movl    $.LC0, %edx
    movl    %eax, 4(%esp)
    movl    %edx, (%esp)
    call    printf
    leave
    ret
    .size    main, .-main
    .ident    "GCC: (Ubuntu 4.4.3-4ubuntu5) 4.4.3"
    .section    .note.GNU-stack,"",@progbits

可見上面程式產生了函式掉用,即引數的壓棧

下面是測試程式碼3:測試行內函數的

test3.c

#include<stdio.h>

inline int sum(int x, int y) __attribute__((always_inline)); //注意這裡加上編譯屬性,讓編譯器始終展開此行內函數,否則編譯出來後跟普通函式沒啥區別

inline int sum(int x, int y)
{
    return x + y;
}

void main()
{
    printf("%d\n",sum(5,6));
}

同樣先預編譯:

gcc -E test3.c -o test3.i

得到下面程式碼: test3.i

....

# 2 "test3.c" 2

inline int sum(int x, int y) __attribute__((always_inline));

inline int sum(int x, int y)
{
    return x + y;
}

void main()
{
    printf("%d\n",sum(5,6));
}

由此可見,行內函數的展開不管預編譯的事情,明顯是在編譯時展開的

下面將 test3.i編譯

gcc -S tets3.i -o test3.S

得到下面的程式碼 test3.S:

    .file    "test4.c"
    .text
.globl sum
    .type    sum, @function
sum:
    pushl    %ebp
    movl    %esp, %ebp
    movl    12(%ebp), %eax
    movl    8(%ebp), %edx
    leal    (%edx,%eax), %eax
    popl    %ebp
    ret
    .size    sum, .-sum
    .section    .rodata
.LC0:
    .string    "%d\n"
    .text
.globl main
    .type    main, @function
main:
    pushl    %ebp
    movl    %esp, %ebp
    andl    $-16, %esp
    subl    $32, %esp
    movl    $5, 28(%esp)
    movl    $6, 24(%esp)
    movl    24(%esp), %eax
    movl    28(%esp), %edx
    leal    (%edx,%eax), %eax  #這裡可以看出,沒有產生函式呼叫,而是將sum的程式碼展開在此
    movl    %eax, %edx
    movl    $.LC0, %eax
    movl    %edx, 4(%esp)
    movl    %eax, (%esp)
    call    printf
    leave
    ret
    .size    main, .-main
    .ident    "GCC: (Ubuntu 4.4.3-4ubuntu5) 4.4.3"
    .section    .note.GNU-stack,"",@progbits

上面可以清楚的看到,編譯器將行內函數展開到了函式的調用出,並減少了函式呼叫產生的效率和壓棧操作,所以效率比普通函式呼叫要高一些

但是比起巨集直接轉換為立即數肯定還是要低點,畢竟行內函數展開後,程式碼量都比巨集展開的多,很明顯。而巨集的話在預編譯的時候就已經被展開了,在編譯時期就是

當成一個普通表示式處理,而無關任何函式呼叫的事情。

好,現在已經論證完,巨集,普通函式,行內函數的差異,以及三者的執行速度問題了。關於三者什麼時候用,什麼情況下用,後面再討論吧。

相關推薦

c++中普通函式的區別

我們都知道編譯的最終產品是可執行程式——— 由一組由機器語言指令組成,在執行程式時,作業系統將這些指令載入到計算機記憶體中。因此,每一組指令都有一個特定的記憶體地址。 一.普通函式的呼叫 a.當代碼執

含引數的巨集函式的區別

含引數的巨集與函式的區別 1、巨集替換不佔執行時間,只佔編譯時間;而函式呼叫則佔執行時間(分配單元、保留現場、值傳遞、返回),每次執行都要載入,所以執行起來比較慢一些。 2、 定義巨集的時候不要在巨集及其引數之間鍵入空格,因為巨集替換的時候會把你不經意打的空格當作巨集的一部分進去

c/c++區別(一)函式的預設值 函式過載 c/c++介面呼叫 const在c/c++的區別

c/c++ 的區別 一.函式的預設值 在C語言裡函式的引數是不能夠帶預設值的。比如int func(int a, int b = 1);這樣的宣告就是不正確的。但是在C++中上述的宣告是被允許的   函式的預設引數值,即在定義引數的時候同時給它一個初始值。在呼叫函式的時候,

個人C++速成筆記(1) -- C++與C不一樣的地方:、預設引數、函式過載、函式模板、庫函式的呼叫

之前學過C,現在想稍微學習下C++,由於上班,只能利用平時的空閒時間學習,記錄一下學習歷程,激勵自己有始有終,部落格內容主要記錄C與C++不同的地方。                    

定義的成員函式

之前看書的時候沒有注意到這點,今天再翻primer發現了這句話,感覺好神奇,特意來做個筆記!在類內定義的成員函式預設為inline,不論前面是否加了inline,當然,是否內聯還是取決於編譯器啦~而在

C++巨集普通函式執行速度以及三者差異

#include <boost/timer.hpp>#define  _SUM(x,y) x+yusing std::cout;using std::endl;using boost::timer;const int MAX_ARR_SIZE = 50000;i

C/C++之巨集普通函式的區別

轉載:https://www.cnblogs.com/ht-927/p/4726570.html C/C++之巨集、行內函數和普通函式的區別 行內函數的執行過程與帶引數巨集定義很相似,但引數的處理不同。帶引數的巨集定義並不對引數進行運算,而是直接替換;行內函數首先是函式,這就意味著函式的很多

c++中函式的引數傳遞和預設實參的理解

1.引數傳遞   1)函式呼叫時,c++中有三種傳遞方法:值傳遞、指標傳遞、引用傳遞。 給函式傳遞引數,遵循變數初始化規則。非引用型別的形參一相應的實參的副本初始化。對(非引用)形參的任何修改僅作用域區域性副本,並不影響實參本身。 為了避免傳遞副本的開銷,可將形參指定為引用型別。對引用形參的

c++——函式特性函式過載簡單解釋

有預設引數值的引數必須在引數表的最右端 正確示例 void fun(int i;int j=1;int k=10); 錯誤示例 void fun(int i;int j=1;int k); 一般編譯器通過率高的是: 宣告寫預設值;定義不寫預設值 如下示例:

C++語言特性:建構函式,解構函式,虛擬函式,,靜態成員函式,過載覆蓋隱藏

C++中class預設屬性為private, struct預設屬性為public 建構函式:        1.初始化物件成員;        2.建立類物件;        由於虛擬函式是建立在物件的基礎上的,因此建構函式不能宣告為虛擬函式;虛擬函式是在執行的時候才識別,

巨集定義記憶體對齊型別轉換

巨集 與 inline的區別 存在的價值,兩者都是文字替換,降低程式跳轉次數,提高效率 1. define 是預處理命令,無法除錯 ,最簡單文字替換,     inline 是編譯期替換,可以除錯, 存在引數型別檢查 2. 使用inline的時候,函式必須定義   直接定義的函式

C++中的預設引數和佔位引數

說明:裡面的示例程式碼有些被註釋掉了,想執行時要去掉註釋 #include "iostream" using namespace std; /**      內聯函數出現的原因:     C++中的const常量可以替代巨

預設引數函式過載引用...

目錄 預設引數 (預設引數) 函式過載 引用 常引用 行內函數 基於範圍的for迴圈 指標空值---nullptr 預設引數 (預設引數)          定義:指當函式呼叫中省略了實引數時自動使用的一個值。

函式的預設值函式過載

函式預設值 c++在函式方面比c多了個預設值,實際是當給函式不傳引數是該函式就會使用預設值對引數進行賦值,當然預設值是從右向左依次賦值 void sum(int a,int b=2,int c=3) {} int main() { sum(1);//因為a無預設值,所以要傳引數 } 函式預設

C++ 普通巨集的區別

巨集的使用:    因為函式的呼叫必須要將程式執行的順序轉移到函式所存放在記憶體中的某個地址,將函式的程式內容執行完後,再返回到轉去執行該函式前的地方。這種轉移操作要求在轉去執行前要儲存現場並記憶執行的地址,轉回後要恢復現場,並按原來儲存地址繼續執行。因此,函式呼叫要有一定

巨集普通函式的區別

行內函數是程式碼被插入到呼叫者程式碼處的函式。如同 #define 巨集,行內函數通過避免被呼叫的開銷來提高執行效率,尤其是它能夠通過呼叫(“過程化整合”)被編譯器優化。     行內函數和巨集很類似,而區別在於,巨集是由前處理器對巨集進行替代,而行內函數是通過編譯器控制來實現的。而且行內函數是真正的函式,只

4、【C++】靜態成員變數/靜態成員函式//友元函式/友元類/友元成員函式

一、靜態成員     我們可以使用 static 關鍵字來把類成員定義為靜態的。當我們宣告類的成員為靜態時,這意味著無論建立多少個類的物件,靜態成員都只有一個副本。     靜態成員在類的所有物件中是共享的。如果不存在其他的初始化語句,在建立第一個物件時,所有的靜態資料都會被初始化為

C++關鍵字、名稱空間、函式過載、預設引數、、引用

一 .C++入門 1.C++關鍵字 2.名稱空間 3.C++輸入&輸出 4.預設引數 5.函式過載 6.引用 7.行內函數 8.auto關鍵字 9.基於範圍的for迴圈 10.指標空值nullptr&nullptr_t 二. 正文 1.C++關鍵字(C++98)   

C語言 inline與帶參巨集

C語言 inline行內函數與帶參巨集 一、簡述         簡單的介紹inline行內函數、帶參巨集的作用。 二、函式的執行與呼叫         函式執行:會將之前的棧的頂,棧基址壓棧,並在棧中開

C++:名稱空間、預設引數、函式過載、引用、

一.名稱空間           在C/C++中,變數、函式和類都是大量存在的,這些變數、函式和類的名稱都將作用於全域性作用域中,可能會導致很多衝突,所以我們就選擇使用名稱空間。         &nb