ソフトウェア(計算)の効率と変数の使用に関するプログラミングの原則


8

私は古典的な訓練を受けた心理学者であり、プログラマーではないため、特にプログラムの効率性や特定のベストプラクティスに関して、この場合は変数の使用に関して、プログラミングのより高度な側面が私から逃れることがあります。

ここにいくつかの擬似コードがあります:

var a;
var b;
var c;
function GetSomeInformation() {
    returns "XYZ";
}

a = GetSomeInformation();
b = GetSomeInformation();
c = GetSomeInformation();

だから私の質問は:
データを一度変数に格納して同じ関数を繰り返し呼び出すのではなくそれを参照することは多かれ少なかれ効率的(または同じ)ですか?

IE、このコードはより効率的ですか?

var results = GetSomeInformation();
a = results;
b = results;
c = results;

もしそうなら、この効率の向上または低下は一般的に言語間で同じですか、それとも言語によって異なりますか?あるしきい値それはその逆繰り返し関数呼び出しまたはバイスを使用するのではなく、変数に名前を付けるために良くなるどこ?どのような側面で効率が変わる可能性がありますか(たとえば、それがクラスのメンバー関数であるか、グローバルスコープの通常の関数であるかどうかに違いはありますか?)等

可能であれば、そのような概念がC ++ / MFCダイアログにどのように適用されるかを具体的に知りたいと思います。それは、そのフレームワークでコードを書いていたときに生じたものです。

// define pointers to the items in my form
CListBox *pLISTBOX = (CListBox*) GetDlgItem(LISTBOX);
CStatic *pSTATIC_A = (CStatic*) GetDlgItem(STATIC_A);
CStatic *pSTATIC_B = (CStatic*) GetDlgItem(STATIC_B);
CEdit *pEDIT_BOX_A = (CEdit*) GetDlgItem(EDIT_BOX_A);
CEdit *pEDIT_BOX_B = (CEdit*) GetDlgItem(EDIT_BOX_B);
int SelectedIndex = pLISTBOX->GetCurSel();
pSTATIC_A->SetWindowText(pLISTBOX->GetItemData(SelectedIndex));
pSTATIC_B->SetWindowText(pLISTBOX->GetItemData(SelectedIndex));
pEDIT_BOX_A->SetWindowText(pLISTBOX->GetItemData(SelectedIndex));
pEDIT_BOX_B->SetWindowText(pLISTBOX->GetItemData(SelectedIndex));

回答:


10

あなたの質問に答えるために:データを変数に格納してそれを参照することは一般により効率的です。追加のローカル変数は安価です。

簡単な例から始めて、次のようなコードがあるとします。

x = 5;
y = x*x + 1;
z = x*x + 2;

上記のコードを見て、CPUがそれを段階的に実行しているように見せかけると、CPUは同じ値の2倍の乗算を実行しますx。これはほとんどの場合、乗算を1回実行するよりも効率的ではありません。

x = 5;
x2 = x*x;
y = x2 + 1;
z = x2 + 2;

現在、最新のコンパイラーは、ほとんどの場合、共通部分式除去と呼ばれる最適化を行っていx2ます。これには、上記の例と同じ抽出効果があります。コンパイラーがあなたの背中を持っているので、あなたはしばしばそれを心配する必要はありません。

とは言っても、などの変数を使用するx2と、次の行の複雑さが大幅に軽減される可能性があるため、読みやすさの理由でそのような変数を導入しても問題はありません。

MFCコードの場合、を繰り返し呼び出していpLISTBOX->GetItemData(SelectedIndex)ます。これはOSを呼び出して追加のデータを取得する関数呼び出しであるため、コンパイラーはこれに対して共通の部分式の除去を行うことができません。代わりに、新しい変数を導入して、呼び出しを1回だけ行う必要があるようにします。

int SelectedIndex = pLISTBOX->GetCurSel();
const char *data = pLISTBOX->GetItemData(SelectedIndex);
pSTATIC_A->SetWindowText(data);
pSTATIC_B->SetWindowText(data);
pEDIT_BOX_A->SetWindowText(data);
pEDIT_BOX_B->SetWindowText(data);

1
呼び出された関数(GetSomeInformation)が純粋、コンパイラがこの事実を認識している場合にのみ、CSEが元の例で機能することを追加する場合があります。それ以外の場合、コンパイラーはそれを3回呼び出して、予期したとおりに副作用が発生することを確認する必要があります。
tdammers 2013

1
オプティマイザを引用するための+1。OPは、ソースコードがオブジェクトコードの作成方法に関するコンパイラへのアドバイスにすぎないことを理解する必要があります。
ロスパターソン

3
  1. はい、ほとんどの言語で、2番目のコードは最初のコードよりわずかに効率的です。
  2. それはおそらく実用的な違いはありません。
  3. それはおそらくない読みやすさの違いを作るので、クリーンなコードで行きます。

また、優れたコンパイラー(JavaのJITを含む)は、繰り返されるメソッド呼び出しをインライン化し、繰り返し使用される値をキャッシュするため、2つの例はおそらく同等のパフォーマンスを発揮します。

以下の経験則を参照してください。

それを機能させる、正しくする、速くする...この順序で。-ケントベック

最適化の最初のルール:しないでください。

最適化の2番目のルール:まだしないでください。

最適化の3番目のルール:最適化前のプロファイル

時期尚早の最適化は、すべての悪の根源です。-ドナルドクヌース

ほとんどの場合、プログラムの時間の90%以上がコードの10%未満に費やされ、コードをプロファイリングしないと、どれが10%であるかがわかりません。フィードバックが得られるまでは心配しないでください。ランタイムよりもプログラマの時間を最適化する方がはるかに価値があります


3

コンパイラがまだ最適化していない場合は、通常、計算された値をローカル変数に格納する方が効率的です。実際、これはmemoizationと呼ばれる手法で、関数自体に組み込まれる場合があります。

ただし、ほとんどの場合、効率の向上は無視できるほど小さいと見なされます。読みやすさは、通常、正確さの後の主な関心事です。

そうは言っても、ローカル変数を使用すると、読みやすくなり、両方の利点を最大限に引き出すことができます。あなたの例を使用して:

const char* text = pLISTBOX->GetItemData(SelectedIndex);
pSTATIC_A->SetWindowText(text);
pSTATIC_B->SetWindowText(text);
pEDIT_BOX_A->SetWindowText(text);
pEDIT_BOX_B->SetWindowText(text);

これは人間の読者にははるかに明確です。すべての行でその長い関数呼び出しを読む必要がないだけでなく、すべてのウィジェットがまったく同じテキストを受け取っていることは明白です。


2

変数は基本的に無料ですが、関数呼び出しのコストは不明なので、常に変数を使用します。


1

私が従う一般的な原則は、私はしていない場合ということです知っている機能がどのように実装されるか、それは可能性が高価なことと、私があれば知っている私はちょうどそれをローカルに保存し、一度私はその関数から値を必要とすること。コード例は次のようになります。

// define pointers to the items in my form
//...
int SelectedIndex = pLISTBOX->GetCurSel();
String text = pLISTBOX->GetItemData(SelectedIndex); //Ok, so maybe String isn't a valid type here but you get the idea...
pSTATIC_A->SetWindowText(text);
pSTATIC_B->SetWindowText(text);
pEDIT_BOX_A->SetWindowText(text);
pEDIT_BOX_B->SetWindowText(text);
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.