2017-02-16 4 views
2

私がコードを書くとき、私はプログラムの論理的に割り切れる部分を "モジュール"を作成することによって別々に保つことを試みます。これを達成するために、NodeJSやPythonを使ったJavascriptのような言語から来るのは本当に簡単です。私は以下の例を提供したパターンでこれを達成する方法を見つけました。 const宣言された構造体で静的メソッド宣言を使用して、コードを整理するための「モジュール」を作成します。コンパイラで構造体の関数呼び出しを最適化できますか?

このテクニックでメソッドを呼び出すコストは、通常、呼び出しごとに1つのアセンブリ命令に過ぎないことに気付きました。

代わりの

movl -8(%rbp), %edx 
movl -12(%rbp), %eax 
movl %edx, %esi 
movl %eax, %edi 
call my_add_method 

「モジュール」技術は、私が見つけたいと思う何

movl $my_add_method, %ecx 
movl -8(%rbp), %edx 
movl -12(%rbp), %eax 
movl %edx, %esi 
movl %eax, %edi 
call *%rcx 

が生成されますと、これらのモジュールを宣言する方法ですが、コンパイルされた出力はちょうど呼び出すと同一でいますそれは即時の名前です。

私が知りたいのは次のとおりです。結果としてASMがあるように

  1. 、いずれかのフラグを持つか、異なる構造を宣言することにより、コンパイラ(gccの)を持ってする方法はあります、コードを最適化同じ?

  2. シンプルコンパイラが最適化するものはありますか?メソッドが存在しない場合、なぜこの種の最適化は一般的に不可能ですか?一般的なケースでは


/** 
* File: main.c 
* Target: x86_64-linux-gnu 
* Compile: gcc main.c -S -o main 
*/ 
#include <stdio.h> 

typedef struct { 
    int (* const add_func)(int, int); 
} MY_MOD_T; 

static int my_add_method(int a, int b) { 
    return a+b; 
} 

const MY_MOD_T Module = { 
    .add_func = my_add_method 
}; 

int main(void) { 
    int a = 5; 
    int b = 6; 

    // substitute these two lines to see the different output like above 
    int result = Module.add_func(a, b); 
    //int result = my_add_method(a, b); 

    printf("%d + %d = %d\n", a, b, result); 
    return 0; 
} 
+0

パフォーマンスの違いを確実に検出できますか? –

+2

最適化フラグ(-O)を試しましたか?出力は非常に異なるでしょう。 '-O0'でasm出力を最適化しようとするのはおそらく無駄を冒すものです。 – Jahaja

+0

いいえ、これはパフォーマンスの問題ではなく、単に好奇心です。私はちょうどこれが最適化するコンパイラのための簡単なことだと思った。 – MatUtter

答えて

1

(構造はすべての定数と静的で考慮して)、名前の関数を呼び出すとまったく同じ関数ポインタ振る舞いを通じて関数呼び出しを行うことは不可能です。

typedef struct { 
    int (* const add_func)(int, int); 
} MY_MOD_T; 

module_derived.hと呼ばれる別のヘッダファイル:

#include "module_interface.h" 

extern const MY_MOD_T Module; 

module_derived.cで派生モジュールの実装:

#include "module_derived.h" 

static int my_add_method(int a, int b) { 
    return a+b; 
} 

const MY_MOD_T Module = { 
    .add_func = my_add_method 
}; 

int module_add_method(int a, int b) { 
    return my_add_method(a, b); 
} 

あなたの例では

は、ヘッダファイルmodule_interface.hを考えます

あなたのメインプログラムは、以下のようなOOK:module_derivedは実際には共有ライブラリである場合

#include <stdio.h> 
#include "module_derived.h" 

extern int module_add_method(int a, int b); 

int main(void) { 
    int a = 5; 
    int b = 6; 

    // substitute these two lines to see the different output like above 
    int result = Module.add_func(a, b); 
    //int result = module_add_method(a, b); 

    printf("%d + %d = %d\n", a, b, result); 
    return 0; 
} 

、実際に関数ポインタの値が逆参照されなければならないという事実を克服することができ、最適化レベルがありません。-O3時:

# Calling named function
movl $6, %esi
movl $5, %edi
call module_add_method
# Calling through module
movl $6, %esi
movl $5, %edi
call *Module(%rip)

ご覧のとおり、モジュール機構を通過する際に余分なオフセット計算と逆参照があります。

ただし、共有ライブラリの場合、このモジュールのオーバーヘッドは、位置独立コード(PLTとGOTオーバーヘッド)によって課されるオーバーヘッドに匹敵します。実際には、プロファイリングがそうでないとオーバーヘッドは心配する価値はありません。その場合、ホット・ファンクション・コールをインライン化する方法を検討する必要があります。

+0

'' '-shared'''を使うと、-Oが使われていても、なぜ私は異なった出力を見てきたのかを説明します。ありがとう! – MatUtter

関連する問題