コードを書くとき、コンパイルされたマシンコードについて考える必要がありますか?


20

たとえば、次のコードがあります。

auto z = [](int x) -> int {
    if (x > 0) {
        switch (x) {
            case 2: return 5;
            case 3: return 6;
            default: return 1;
            }
        }
    return 0;
    };

そして、後でこれを数回呼び出します。asmコードでは、ラムダを使用した外部呼び出しが表示されます。メタプログラミングで勝つかもしれませんが、asmのデバッグとパフォーマンスで負けるのですか?パフォーマンスとデバッグの単純さを確実にするために、最新の言語機能、マクロ、およびその他のメタプログラミングの側面を避ける必要がありますか?


1
コンパイラのバージョンとバンドルされている標準ライブラリによっては、ラムダが実際に非効率的に実装される場合があります。Stackoverflowでこの質問を参照してください。ただし、改善の責任はコンパイラベンダーにあります。
rwong

15
パフォーマンスクリティカルパスを実行している場合を除き、アセンブリコードをデバッグする必要はありません。また、「きれいなコード」!=「良いパフォーマンス」。
BЈовић

インデントを修正してください。試しましたが、空白だけを編集することはできないようです。
クリストファーハンマルストローム

3
@Heather:Ratliffスタイルを使用しているように見えますが、これは今まで見たことがなく、読みにくいと思います。それは確かにあまり知られていないものの一つです。私の印象では、あなたは適切にインデントされていなかった。あなたがそれを読みやすいと思うならば、決して気にしません、私はちょうど同意しません。
クリストファーハマーストローム

1
これifはサンプルコードでは完全に冗長であり、コンパイラはおそらく、不適切な分岐予測を誘惑する理由がないことをキャッチします。
dmckee

回答:


59

コードを書くとき、コンパイルされたマシンコードについて考える必要がありますか?

いいえ、初めてコードを書いたときで、実際の測定可能なパフォーマンスの問題に悩まされていません。ほとんどのタスクでは、これが標準的なケースです。最適化についてあまりにも早く考えることは「時期尚早の最適化」と呼ばれ、D。クヌースがそれを「すべての悪の根源」と呼ぶ正当な理由があります。

はい、実際の証明可能なパフォーマンスのボトルネックを測定し、その特定のラムダ構造を根本原因として特定した場合。この場合、Joel Spolskyの「漏れやすい抽象化の法則」を覚えて、asmレベルで何が起こるかを考えることをお勧めします。ただし、ラムダ構造を「それほど現代的ではない」言語構造に置き換えると(少なくとも、まともなC ++コンパイラを使用する場合)、パフォーマンスの向上がどれほど小さくなるかに注意してください。


2
+1通常のドキュメントごとに簡潔かつ正確で、簡単にフォローできます。
ジミー・ホッファ

同意、非常に明確な答え。
CND

8

lambdaとfunctor-classの選択はトレードオフです。

ボイラープレートの量を最小限に抑え、概念的に関連するコードを(すぐにまたは後で)それを利用する関数内にインラインで記述できるようにすることで、ラムダからの利益はほとんど構文的です。

パフォーマンスの面では、これは、単一の「メソッド」を含むC ++構造体またはクラスであるファンクタークラスよりも悪くありません。実際、コンパイラはラムダを、背後でコンパイラが生成したファンクタクラスと同じように扱います。

// define the functor method somewhere
struct some_computer_generated_gibberish_0123456789
{
    int operator() (int x) const
    {
        if (x == 2) return 5;
        if (x == 3) return 6;
        return 0;
    }
};

// make a call
some_computer_generated_gibberish_0123456789 an_instance_of_0123456789;
int outputValue = an_instance_of_0123456789(inputValue);

コード例では、パフォーマンス面では関数呼び出しと違いはありません。そのファンクタークラスには状態がないため(空のキャプチャ句があるため)、割り当て、コンストラクタ、または破棄が不要です。

int some_computer_generated_gibberish_0123456789_method_more_gibberish(int x)
{
    if (...) return ...;
    return ...;
}

逆アセンブラを使用して重要でないC ++コードをデバッグすることは、常に困難な作業でした。これは、ラムダを使用してもしなくても当てはまります。これは、C ++コンパイラーによる洗練されたコード最適化が原因で、並べ替え、インターリーブ、デッドコードの削除が発生します。

名前をマングルするという側面はやや不愉快であり、ラムダのデバッガーサポートはまだ初期段階にあります。デバッガーのサポートが時間の経過とともに向上することが期待されるだけです。

現在、ラムダコードをデバッグする最良の方法は、ソースコードレベルでのブレークポイントの設定をサポートするデバッガーを使用することです。つまり、ソースファイル名と行番号を指定することです。


3

@DocBrownによる回答に追加するには、最近のCPUは安価ですが、人件費が高いことに注意してください。

プログラムの全体的なコストの中で、ハードウェアは通常、保守のコストと比較してささいなものです。保守のコストは、典型的なプロジェクトの中で最も高価な部分です(開発よりもさらに)。

したがって、パフォーマンスが重要な場合(および保守を検討する必要がある場合を除く)を除き、コードは他のすべてよりも保守を最適化する必要があります。


部分的にのみ真実。コードでO(n ^ 2)(2次)を実行し、O(log(n))(対数)と言ったほうがよい場合、コードを変更してもハードウェアのパフォーマンスが大幅に向上することはありません。元のポスターで指定されている場合、これはほとんどありません。
gnash117

@ gnash117 —はい、コードを何度も実行する場合は正しいです。これを指摘してくれてありがとう。そのような場合、コードを明確に文書化することで、パフォーマンスを向上させながら、保守性を維持できます。
水田ランダウ

「労働は高価です」-正しい。顧客の時間は非常に重要であり、多くの場合高価です。
セラド
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.