2009-08-13 15 views
2

は少しテストです:機能を使用しないとパフォーマンスが向上しますか?ここ

function inc(n:integer):integer; 
begin 
    n := n+1; 
    result := n; 
end; 


procedure TForm1.Button1Click(Sender: TObject); 
var 
    start,i,n:integer; 
begin 
    n := 0; 
    start := getTickCount; 
    for i := 0 to 10000000 do begin 
    inc(n);//calling inc function takes 73 ms 
    //n := n+1; writing it directly takes 16 ms 
    end; 
    showMessage(inttostr(getTickCount-start)); 
end; 
+1

あなたのベンチマークは、関数にとって少し不公平です。オーバーヘッドを減らすためにこれを試してください: function inc(var n:integer); begin 結果:= n + 1; end; – Tihauan

答えて

14

はい、関数を呼び出すことはオーバーヘッドを紹介します。関数を呼び出す前に、現在の状態(命令が次に実行されることを計画していた)を保存し、関数のパラメータをコピーする必要があります。余分な作業と余分な時間が必要です。

これは、インライン化が役立つところです。コンパイラがサポートしている場合は、コールサイトで関数コードを直接呼び出すことができ、オーバーヘッドを避けることができます。周囲のコードを適切に最適化すれば、生成されるコードの量を減らすことさえできます。

これは、機能を回避する必要があるわけではありません。ほとんどの場合、関数本体は、呼び出しを整理するのに必要な時間よりもずっと長く実行されます。非常にまれなケースでのみ、オーバーヘッドは最適化する価値があります。これはプロファイラの助けなしには決して行われません。そうしないと、時間が無駄になり、多くの場合、維持不能なコードが多く発生します。

+22

私が読んだところで最高の見積もりは次のようになります。パフォーマンスを向上させる機能を避けることは、体重を軽くすることです。* –

+0

sharptoothのポイントに追加するには:機能を避けることを心配しないでください。関数をインラインで宣言することでインラインで関数を作成することはできますが、これはほとんど必要ありません。通常、コンパイラは必要に応じて関数をインライン化することにします。 – Martijn

+0

実際には、** CAN **呼び出しを追加すると、グローバルオーバーヘッドが減少する場合があります。 (私の答えのリンクを参照) –

5

関数を呼び出す(通常作業している言語のいずれか)には、コンテキストを保存したり、パラメータを何らかのスタックにプッシュしたり、関数自体を呼び出したり、パラメータを読み込んだりするなど、結果をどこかに戻し、関数から戻り、戻り値を抽出します。

したがって、関数を呼び出すのは一般的にオーバーヘッドがあることを意味します。

しかし、機能の主なポイントは、コードの一部を再利用している:(多分それは実行時より数マイクロ秒かかりますが、あなたは唯一の10の代わりに、一度、いくつかのコードを記述しなければならない場合やより多くの時間)、大きな利益があります。そのコードは、維持するのがはるかに簡単になり、長期的には非常に重要です。

例として提供したコードのような実際のコードの一部には関数を使用しないことをお勧めします。(使用している言語が何らかのインライン展開を提供する場合を除いて - 私が正しく覚えていれば、Cについては、確かに、デルファイについてはわからない):機能を呼び出すのオーバーヘッドは、コードの行の数に比べて重要です、関数は、書き込みからあなたを保存します(ここでは: ^^)。
しかし、コードの大部分については、関数のコードを実行するのに要する時間に比べてオーバーヘッドがはるかに小さくなります。

+0

インライン展開は言語に依存しないため、コンパイラに依存します。そして、はい、デルファイコンパイラはそれを行います。 –

+0

DelphiコンパイラはD2006の機能を追加しました –

+0

D2005の修飾子はすでにafaikです。しかし、IIRCでは、インライン化できる症例は比較的少ない。 2.0シリーズ以来FPCをサポートしています。(2005) –

0

私が以前に言いましたが、はい、そうです。あなたが書くコードのすべての行があります。関数は、レジスタなどの現在の状態を格納しておく必要があります。

しかし、オーバーヘッドは非常に小さく、最適化は何も意味しません。 redableな構造​​化されたコードを持つことがより重要です。ほとんどいつも。まれに、すべてのナノ秒が重要であるかもしれませんが、今は想像できません。 Delphiのプログラムでパフォーマンスに関する一般的なガイドラインはこちら

ルック:

http://effovex.com/OptimalCode/opguide.htm

+0

いいえ、コード行は、常に比例してパフォーマンスペナルティに変換されるわけではありません。特に関数の場合、インライン展開があります。 –

+0

確かに、私は十分に具体的ではありません:)しかし、それは一般的なルールです。 – Runner

0

はちょうどデルファイへの具体的ないくつかのコメントを追加したい:

  • I)は、(私はGetTickCountをより覚えてしまったと思いますこの種のテストを行うには最低限の分解能が必要です。 (+/- 10-15ms)。 QueryPerformanceCounter()を使用すると、より良い結果が得られます。小さな関数のための

  • は、あなたがしなければならない場合

  • (ヘルプの検索)INLINEを使用しますが、funcitonを取る何実質のために知っておくと(...、プロセスループ内のデータconvertionを)多くの時間と呼ばれますそれについて何かしてください、プロファイラを使用!私はhttp://www.prodelphi.de/を使用しています。これは非常にシンプルで非常に便利で、価格は他のプロファイラと比較して非常に正しいです(つまり、500ユーロではなく+/- 50ユーロ)。

  • デルファイでは、inc()関数です。それは "n:= n + 1"より高速です。 (inc()は実際には関数ではないので、asmでコンパイラに置き換えられます。つまり、関数inc()のソースコードはありません。

+2

コンパイラは 'n:= n + 1'を最適化することができます。実際には' inc'よりも遅くはありません。 –

+0

私はasmコードをチェックしていないことを告白しますが、内側のループや他の非常によく使われるコードのためにn:= n + 1のinc()insteedを使うべきです。 それ以来、コンパイラが改善されているかもしれません... – Loda

5

時期尚早の最適化は諸悪の根源である...
は、既知の特徴(ここでは、内蔵擬似(マジック)手続きInc)を使用して正しいと保守性のコードを書き、ベンチマークそれ、どこでリファクタリングパフォーマンス上の理由(必要な場合)のために必要です。

私はそのケースの99.9%で、関数やプロシージャを呼び出すことを避けることが解決策ではないと確信しています。プロシージャの呼び出しを追加

Here is an exampleは実際には、最適化されています。

+0

"逆に、システムレベルでソフトウェアを設計する場合、パフォーマンスの問題は常に最初から考慮する必要があります" 参照:http://stackoverflow.com/questions/445648/ the-great-optimized-rift/445719#445719 – lkessler

1

ボトルネックがある場合にのみ最適化します。

現在のコードは、ケースの約99.9%で完璧です。

遅い場合は、プロファイラを使用してボトルネックを指摘してください。 ボトルネックがinc関数内にあるように見える場合は、関数 'inline'を使用して関数にインラインで入力することができます。

私はFrancoisと全く同意します。

0

すべての良いコメント。

関数は有用であると考えられています。そのため、関数は言語に属しています。彼らは名目上の費用があれば、それを支払って彼らが提供する効用を得ることを前提としています。

ここでは、誰がそれらを書いても、特にあなた以外の誰かがそれらを書いたとしても、関数に関する実際の問題があります。

彼らは何をすべきかについて暗黙の契約をしていますが、どれくらいの時間を取るべきかについての契約はありません。

通常、この関数を書く人は、「この関数は何か有益なことをするので、呼び出す人はそれを尊重し、控えめに使用します」と考えます。

これを呼び出す人は、「この関数は1回の呼び出しで非常に多くのことを行うので、コードを何度も呼び出して本当にクリーンでパワフルにすることができます」と考えています。

ここで、複数の抽象レイヤーを使用すると、この効果は複合的な関心のように作用します。

したがって、関数の実際のパフォーマンスの問題は、呼び出しのコストではなく、プログラマの心理学であり、指数関数的な減速につながります。関数呼び出しの中で最も高価な部品の

Fortunately, experience in performance tuning can ameliorate this problem.

1

一つの結果を返しています。

プログラムをモジュール化しておきたいが、少し時間を節約したい場合は、関数をプロシージャに変更し、varパラメータを使用して結果を取得します。だからあなた例えば

procedure inc(var n:integer); 
begin 
    n := n+1; 
end; 

はあなたINC機能を使用するよりもかなり高速である必要があります。


また、あなたの例のループでは、次の文があります。

inc(n) 

をが、これは、nの値は更新されません。ループが終了しますとnあなたの代わりに必要なものは0の値を持つことになります:

あなたのタイミングについては
n := inc(n); 

、あなたは上の最適化を持っているのですか?あなたがそうした場合、それはあなたのものであるタイミングではないかもしれません。 nの値はプログラムでは使用されておらず、最適化されている可能性があります。

nがタイミングに使用されていることを確認するには、showMessage行にnの値を表示するだけです。


最後に、incは組み込みプロシージャです。組み込みプロシージャと同じ関数名を使用するのは良い方法ではありません。なぜなら、実行されているプロシージャ(自分または組み込みプロシージャ)が疑わしいからです。

関数の名前をmyincに変更し、組み込みのincプロシージャ自体で3番目のテストを実行して、n:= n + 1より高速かどうかを確認します。