web-dev-qa-db-ja.com

すっきりと読みやすいコードと高速で読みにくいコード。いつ境界線を越えるか?

コードを書くときは、常にコードをできるだけクリーンで読みやすいものにするよう努めています。

時々、ラインを越えてニースのクリーンなコードから少し醜いコードに切り替えて高速化する必要があるときがあります。

いつその線を越えてもいいですか?

67
Ken Cochrane

あなたがラインを越えるとき

  • あなたは測定あなたのコードはその意図された使用には遅すぎるであることを持っています。
  • コードをいじる必要のない代替の改善を試みました。

これが実際の例です。私が実行している実験システムは、データの生成が遅すぎ、実行ごとに9時間以上かかり、CPUの40%しか使用していませんでした。コードを混乱させるのではなく、すべての一時ファイルをメモリ内ファイルシステムに移動しました。醜くないコードの新しい行を8行追加し、CPU使用率が98%を超えました。問題が解決しました;醜さは必要ありません。

118
Norman Ramsey

それは誤った二分法です。コードを高速およびに簡単に維持できます。

それを行う方法は、特にできるだけ単純なデータ構造で、それをきれいに書くことです。

次に、時間の浪費がどこにあるかを調べ(それを実行することにより、作成した後で作成した前ではなく)、1つずつ修正します。 (これは例です)

追加:時間とメモリのトレードオフ、速度と保守性のトレードオフなど、トレードオフについて常に耳にしますか?このような曲線が存在する可能性はありますが、特定のプログラムが曲線上またはその近くのどこかにあると想定しないでください。

曲線上にあるすべてのプログラムは、(特定の種類のプログラマーに与えることによって)非常に遅くなり、保守性が低下する可能性があります。そのようなプログラムには、より速く、より保守しやすくするための十分な余地があります。

私の経験では、多くのプログラムがそこから始まります。

58
Mike Dunlavey

私のOSSの存在下で、パフォーマンスを目的とした多くのライブラリー作業を行っています。これは、呼び出し元データ構造(つまり、ライブラリーの外部)に深く関連しています、(設計上)着信タイプに対する権限はありません。ここで、これをperformantにする最良の方法はメタプログラミングです。これは(私が.NETの世界にいるため)IL-emitを意味します。これは醜い、醜いコードですが、very高速です。

このように、私はlibraryコードがapplicationコード、単になので、入力に対する制御が少ない(または多分ない)なので、いくつかのタスクを実行する必要がありますさまざまなメカニズムを通じて。または、先日私がそれを表現したように:

「狂気の断崖を越えてコーディングする、だからあなたはする必要がない

現在applicationのコードは少し異なります。これは、「正規」の(正気な)開発者が通常、共同作業や専門的な時間の多くに投資しているためです。それぞれの目標と期待(IMO)は少し異なります。

IMO、高速である可能性があることを示唆する上記の答えおよび保守が容易なのは、application開発者がデータ構造をより詳細に制御でき、メタプログラミングなどのツールを使用していないコード。そうは言っても、メタプログラミングを行うにはさまざまな方法があり、狂気のレベルとオーバーヘッドのレベルが異なります。その分野でも、適切な抽象化レベルを選択する必要があります。しかし、積極的に、積極的に、本当に予期しないデータを絶対最速で処理したい場合。醜くなるかもしれません。それに対処する; p

31
Marc Gravell

コードをプロファイリングし、それが実際に大幅なスローダウンを引き起こしていることを確認した場合。

27
Joel Rein

クリーンなコードは、高速実行コードと必ずしも排他的ではありません。通常、読み取りが困難なコードは、実行速度が速いためではなく、書くのが速いために書かれました。

変更を加えて実際に何かが改善されることは確かにわからないため、「ダーティ」コードを高速化するためにコードを記述することは、間違いなく賢明ではありません。クヌースはそれを最もよく置きました:

「私たちは小さな効率を忘れるべきです。たとえば、約97%の時間を言います。時期尚早な最適化はすべての悪の根源です。しかし、その重要な3%で機会を逃してはなりません。 。優れたプログラマーは、そのような推論によって自己満足に陥ることはなく、重要なコードを注意深く検討するのが賢明ですが、そのコードが識別された後でのみ。」

つまり、最初にコードをクリーンに記述します。次に、結果のプログラムのプロファイルを作成し、そのセグメントが実際にパフォーマンスのボトルネックになっていないかどうかを確認します。その場合は、必要に応じてセクションを最適化し、最適化を説明するために、ドキュメントのコメント(元のコードを含む可能性があります)をたくさん含めてください。次に、結果をプロファイリングして、実際に改善したことを確認します。

13
tylerl

質問には「高速読みにくいコード」と書かれているため、簡単な答えは決してありません。読みにくいコードを書くための言い訳は決してありません。どうして? 2つの理由。

  1. 今夜家に帰る途中でバスにぶつかったらどうなりますか?または(より楽観的に、より一般的には)このプロジェクトを中止して、別のプロジェクトに再割り当てしましたか?もつれたコードの混乱であなたが想像した小さな利点は、誰もそれを理解できないという事実を完全に上回っています。これがソフトウェアプロジェクトにもたらすリスクは、誇張するのが困難です。私はかつて大手メーカー [〜#〜] pbx [〜#〜] のメーカーと仕事をしていました(オフィスで働いているなら、机の上に電話が1つあるでしょう)。彼らのプロジェクトマネージャーは、ある日、彼らのコア製品(標準のLinuxボックスを完全な機能を備えた電話交換機に変える独自のソフトウェア)が社内では「ブロブ」として知られていると言った。もう誰もそれを理解していません。彼らは常に新しい機能を実装しました。彼らはコンパイルを押してから立ち戻り、目を閉じて20まで数え、指で覗いてそれが機能するかどうかを確認しました。もはや管理しなくなったコア製品を必要とするビジネスはありませんが、それは恐ろしく一般的なシナリオです。
  2. しかし、最適化する必要があります!OK、この質問に対する他の回答で優れたアドバイスをすべて実行しました:コードがパフォーマンステストケースに失敗しています、あなたはそれを注意深くプロファイリングし、ボトルネックを特定し、解決策を考え出しました...そしてそれはいくつかの bit-twiddling を伴います。罰金:さあ、先に進んで最適化しましょう。しかし、ここに秘密があります(そして、これに腰を下ろしたいかもしれません):最適化とソースコードサイズの縮小は同じではありません。コメント、空白、角括弧、意味のある変数名はすべて、読みやすさを大幅に向上させるものであり、コンパイラーがそれらを破棄するため、費用は一切かかりません。 (または、JavaScriptのような非コンパイル言語を作成している場合-そしてそうです、JavaScriptを最適化する非常に正当な理由があります-それらは compressor で処理できます。)窮屈でミニマリストの長い行コード(muntooが投稿したような here )は最適化とは何の関係もありません。それは、できるだけ多くのコードをできるだけ少ない文字にパックすることによって、どれほど賢いかを示すプログラマです。それは賢いことではありません、それは愚かです。真に賢いプログラマとは、自分の考えを他の人に明確に伝えることができるプログラマです。
10
Mark Whitaker

使い捨てコードの場合。つまり、1回限りの計算またはタスクを実行するスクリプトを記述し、そのような確実性を知っていれば、迷わずに 'rm source-file'を実行できるということを二度と実行する必要がないということですその後あなたは醜いルートを選ぶかもしれません。

そうでなければ、それは誤った二分法です-あなたがそれをより速くするために醜くする必要があると思うなら、あなたはそれを間違っています。 (または、適切なコードが何であるかについての原則は修正が必要です。gotoを使用することは、問題に対する適切な解決策である場合、実際には非常に洗練されています。しかし、それがめったにありません。)

4
maaku

市場におけるパフォーマンス低下の推定コストが、問題のコードモジュールのコードメンテナンスの推定コストよりも大きい場合。

人々はまだ手でコード化されたSSE/NEONなどをひねっています。今年の人気のCPUチップで競合他社のソフトウェアを試して打つためのアセンブリ。

3
hotpaw2

適切なドキュメントとコメントを付けることで、読みにくいコードを理解しやすくすることができることを忘れないでください。

一般に、目的の機能を実行する読みやすいコードを記述した後でプロファイルを作成します。ボトルネックは、それをより複雑に見えるようにする必要があるかもしれませんが、自分で説明することによって修正します。

3
Carlos

私にとっては、それは安定性の比率です(コンクリートで固め、オーブンで焼いた粘土を石で固め、永久インクで書いたように)。コードが不安定になるほど、将来的に変更する必要が生じる可能性が高くなるため、生産性を維持するために、ウェットクレイのようにコードをより柔軟にする必要があります。また、読みやすさではなく、柔軟性を強調します。私にとって、コードの変更のしやすさは、コードの読みやすさよりも重要です。コードは読みやすく、悪夢のように変化する可能性があります。実装が変更されて悪夢のようである場合、実装の詳細を読み、簡単に理解できるのはどのような用途ですか。単なる学術的な演習でない限り、通常、本番環境のコードベースのコードを簡単に理解できるようにすることは、必要に応じてより簡単に変更できるようにすることを目的としています。変更が難しい場合、読みやすさのメリットの多くはウィンドウの外に出ます。可読性は一般に柔軟性のコンテキストでのみ役立ち、柔軟性は不安定性のコンテキストでのみ役立ちます。

当然ながら、コードの読みやすさや難しさには関係なく、コードを維持するのが最も難しい場合でも、変更する理由がなければ問題は発生しません。使用するだけです。また、特にパフォーマンスが最も重視される傾向がある低レベルのシステムコードの場合は、このような品質を実現することができます。まだ定期的に使用しているCコードがありますが、80年代後半から変更されていません。それ以来、変更する必要はありません。コードは曖昧で、ビットをいじる時代に書かれていて、私はそれをほとんど理解していません。しかし、それは今日でもまだ適用可能であり、十分に活用するためにその実装を理解する必要はありません。

安定性を向上させる1つの方法は、テストを徹底的に記述することです。もう1つはデカップリングです。コードが他に依存していない場合、コードを変更する唯一の理由は、コード自体を変更する必要があるかどうかです。場合によっては、少量のコードの重複がデカップリングメカニズムとして機能し、安定性を劇的に改善して、引き換えに、他のものから完全に独立したコードを取得した場合に、そのトレードオフを価値のあるものにすることができます。これで、そのコードは外界への変更に対して無防備です。一方、10個の異なる外部ライブラリに依存するコードには、将来変更される理由が10倍あります。

実際に役立つもう1つのことは、サードパーティライブラリの場合と同様に、ライブラリをコードベースの不安定な部分から分離し、場合によっては個別にビルドすることです(同様に、使用せず、変更せず、少なくともチーム)。そのような組織であれば、人々がそれを改ざんするのを防ぐことができます。

もう1つはミニマリズムです。コードが実行しようとする回数が少ないほど、コードがうまく機能する可能性が高くなります。モノリシックデザインはほとんど恒久的に不安定です。機能が追加されるほど、不完全に見えるようになります。

マイクロチューニングされた並列化されたSIMDコードのように、変更が難しくなることが避けられないコードを書くことを目指すときはいつでも、安定性が主な目標です。コードを変更する必要がない可能性を最大化することにより、コードを保守することの難しさを打ち消し、将来的にコードを保守する必要がなくなります。これにより、コードの保守がいかに困難であっても、保守コストがゼロになります。

0
user204677