0

この方法で関数ポインタを定義することを知っています

struct handler_index {
    const char *name;
   int (*handler)();
};

指定されていない (ただし変数ではない) 数と型のパラメーターを持つ任意の関数にハンドラー ポインターを使用できるようにします。

しかし、この定義がコードやメモリの最適化、またはこれと比較して実行時間に影響を与える可能性があるかどうか疑問に思っています:

struct handler_index {
    const char *name;
    int (*handler)(int a, int b);
};
4

2 に答える 2

2

関数ポインターにさらにパラメーターを追加すると、特に関数ポインターにどのように影響するか疑問に思っている場合は、そうではありません。関数ポインターは、パラメーターの数に関係なく、すべて同じサイズです。

このような関数ポインタの呼び出しの効率について疑問がある場合: パラメータを追加すると、引数を渡すコードが生成されます。そうです、それは呼び出しのコードサイズにわずかに影響し、それらの引数を渡すときにCPUがどれだけのILPを引き出すことができるかに応じて、おそらく実行時間に影響します.

最新の呼び出し規約では、いくつかの引数をレジスターで渡すことが多いため、スタックの使用量が増える場合と増えない場合があります。

呼び出しごとに生成されたコードの違いを正確に確認するには、呼び出し規則(ここにリストするには多すぎる!) を読み、コードから生成された asm を確認してください。しかし、実際には、(妥当な範囲内で) さらにパラメーターを追加しても、ほとんど問題にならない程度の小さな効果しか得られないでしょう。

于 2012-12-26T17:58:12.043 に答える
1

Coryが言うように、それが関数ポインターであるか、通常の基本関数であるかは実際には関係ありません[通常の関数がインライン化される場合を除いて、もちろん、関数ポインターは一般的にできません-状況が十分に具体的である場合は私がコンパイラが実際に「ああ、ここでは常に関数Xを呼び出しているので、インラインXを使用できるようにする」ことを実際に理解しているのを見てきました。通常、関数ポインタが構造ではなく関数への引数である場合)。

違いを生むのは、一般的に関数呼び出しに引数を追加することです。プロセッサはこれらの引数をどこかに配置する必要があり、それらがレジスタにある場合でも、値をRIGHTレジスタに取り込むために追加の命令が必要になる場合があります。

ただし、最初の例は非常に悪いです。コードが正しいことをしているというチェックがないからです。

さらに、関数に引数を渡すオーバーヘッドを関数ポインターの呼び出しの多くの時間にするために、かなり病理学的なケースが必要です-うまくいけば、関数はある数値を別の数値に追加するだけではありません。

そうは言っても、多くの引数、特に「取得が難しい」引数を渡すことは本当に悪いことです。グラフィックチップシミュレーターで作業していて、ピクセルシェーダー処理ユニットの一部にデバッグ印刷があり、ほとんど印刷されませんでしたが、7または8引数(デバッグレベル1000などを除く)が必要でした。そうだった)。それぞれの構造からそれらの引数を取り出してスタックに貼り付けるのにかなりの時間がかかり、「if(debuglevel> = 1000)...」を配置して、実際に必要なときにのみ呼び出しが行われるようにし、コードを作成しましたその機能では約40%高速です。

于 2012-12-26T18:39:34.413 に答える