最適化されたコードを読み取り可能なコードに置き換えても大丈夫ですか?


78

既存のコードを拡張/改善する必要がある状況に陥ることがあります。古いコードは非常にスリムですが、拡張するのも難しく、読むのに時間がかかります。

それを最新のコードに置き換えるのは良い考えですか?

少し前に私は無駄のないアプローチが好きでしたが、今では、より高い抽象化、より良いインターフェース、より読みやすく拡張可能なコードのために多くの最適化を犠牲にするほうが良いように思えます。

コンパイラも同様に良くなっているように見えるので、何も言わずstruct abc = {}memsetsに変わり、shared_ptrsは生のポインタをいじるのとほぼ同じコードを生成します。

それでも、スタックベースの配列や、いくつかのあいまいなロジックを持つ古いC関数が表示されることがありますが、通常はクリティカルパスにありません。

どちらかの方法でコードの一部に触れる必要がある場合、そのようなコードを変更することは良い考えですか?


20
可読性と最適化はほとんどの場合反対されません。
deadalnix

23
コメントで読みやすさを改善できますか?
YetAnotherUser

17
OOP化が「近代的なコード」と見なされるのではないかと心配している
James

7
スラックウェアの哲学のように:壊れていない場合は修正しないでください。少なくても、それを行う非常に非常に正当な理由があります
-osdamv

5
最適化されたコードとは、実際の最適化されたコード、またはいわゆる最適化されたコードを意味しますか?
dan04

回答:


115

どこ?

  • Google規模のウェブサイトのホームページでは、許可されません。できるだけ早く物事を保管してください。

  • 1年に1人が使用するアプリケーションの一部では、コードを読みやすくするためにパフォーマンスを犠牲にすることは完全に受け入れられます。

一般的に、作業中のコードの一部の非機能要件は何ですか?アクションを900ミリ秒未満で実行する必要がある場合。特定のコンテキスト(マシン、負荷など)で80%の時間、実際には200ミリ秒未満で実行されます。100%の時間で、パフォーマンスにわずかな影響を与える可能性がある場合でも、コードを読みやすくします。一方、同じアクションが10秒未満に実行されなかった場合は、パフォーマンス(またはそもそも要件)の何が問題なのかを確認する必要があります。

また、読みやすさを改善するとパフォーマンスがどのように低下​​しますか?多くの場合、開発者は時期尚早の最適化に近い動作を適応させています:彼らは読みやすさを向上させることを恐れており、パフォーマンスが大幅に破壊されると信じていますが、より読みやすいコードは同じアクションをさらに数マイクロ秒費やします。


47
+1!数字がない場合は、数字を入手してください。数字を取得する時間がない場合、それを変更する時間はありません。
タクロイ

49
多くの場合、そうではありませんが、開発者は神話と誤解に基づいて「最適化」しています。たとえば、「C」は「C ++」よりも高速であると想定し、バックアップする数値がなくても物事が高速であるという一般的な感覚からC ++機能を回避します。gotoループよりも高速だと思ったC開発者を思い出します。皮肉なことに、オプティマイザーはforループのほうが優れていたため、コードをより遅く読みにくくしました。
スティーブンバーナップ

6
別の答えを追加するのではなく、この答えに+1を付けました。コードの断片を理解することが非常に重要な場合は、それらをよくコメントしてください。私はC / C ++ / Assembly環境で、数十人の貢献者と共に10年前のレガシーコードを使用して働いていました。コードが機能する場合は、そのままにして作業に戻ります。
クリスK

だからこそ、読み取り可能なコードのみを書く傾向があります。いくつかのホットスポットをカットしてパフォーマンスを達成できます。
ルカ

36

通常、いいえ

コードを変更すると、システムの他の場所で予期しないノックオンの問題が発生する可能性があります(固体ユニットと煙テストを実施していない場合、プロジェクトのかなり後まで気付かないことがあります)。私は通常、「壊れていないなら、直さないで」という考え方で行きます。

この規則の例外は、このコードに触れる新しい機能を実装する場合です。その時点で意味がなく、リファクタリングを実際に行う必要がある場合は、リファクタリング時間(およびノックオンの問題を処理するための十分なテストとバッファ)がすべて見積もりで考慮されている限り、それを実行します。

もちろん、特にクリティカルパスエリアの場合は、profile、profile、profileを使用します


2
はい。ただし、最適化が必要であると想定しています。私たちはそれがいつだったかを常に知っているわけではなく、おそらくこれを最初に決定したいと思うでしょう。
ヘイレム

2
@ヘイレム:いいえ、私はコードがそのまま機能すると仮定します。また、コードをリファクタリングすると、システムの他の場所で必ずノックオンの問題が発生すると想定しています(外部依存関係のない些細なコードを扱っている場合を除く)。
デミアンブレヒト

この答えにはいくつかの真実がありますが、皮肉なことに、これはノックオンの問題が開発者によって文書化、理解、伝達されず、注意を払われることもめったにないからです。開発者が過去に発生した問題をより深く理解していれば、何を測定するかを理解し、コードの変更をより自信を持って行うことができます。
-rwong

29

簡単に言うと、それは依存します

  • あなたは本当にあなたのリファクタリング/強化されたバージョンを必要とするか、使用するつもりですか?

    • 即時または長期の具体的な利益はありますか?
    • これは保守性のためだけですか、それとも本当にアーキテクチャ上の利点ですか?
  • 本当に最適化する必要がありますか?

    • どうして?
    • どのようなターゲットゲインを目指す必要がありますか?

詳細に

きれいにした光沢のあるものが必要ですか?

ここでは注意が必要なことがあります。実際の測定可能な利益と、個人的な好みとは関係のないコードに触れる可能性のある悪い習慣との間の限界を特定する必要があります。

より具体的には、これを知っている:

オーバーエンジニアリングのようなものがあります

これはアンチパターンであり、組み込みの問題があります:

  • それは、より拡張可能でありより簡単ではないかもしれない、拡張します
  • 理解するの簡単はないかもしれませんが
  • 最後になりましたが、間違いなく重要なことは、コード全体が遅くなる可能性があることです。

KISSの原則を参照として言及する人もいますが、ここでは直観に反しています。以下の残りの部分で説明するように、答えは必ずしも絶対ではありません。

あなたはそれを必要としません

YAGNI原理は、他の問題と完全に直交していないですが、それは自分自身に質問をすることができます:あなたはそれを必要とするつもりですか?

より複雑なアーキテクチャは、保守性が向上しているように見えることを除けば、本当にあなたに利益をもたらしますか?

壊れていない場合は、修正しないでください

これを大きなポスターに書いて、画面の横、職場のキッチンエリア、または開発会議室に掛けてください。もちろん、自分自身を繰り返す価値のある他の多くのマントラがありますが、この特定のマントラは、「メンテナンス作業」を行い、それを「改善」する衝動を感じるたびに重要です。

コードを読んで理解しようとするときに、コードを「改善」したり、無意識にコードに触れたりすることは自然なことです。それは良いことです、それは私たちが意見を述べられ、内部をより深く理解しようとすることを意味しますが、スキルレベル、知識にバインドされていることです(どうすれば良いかどうかを決めるのはどうですか?以下のセクションを参照してください...)、および私たちがソフトウェアを知っていると思うものについてのすべての仮定...:

  • 実際に、
  • 実際に行う必要がある、
  • 最終的に行う必要があります、
  • そしてそれがどれだけうまくいくか。

本当に最適化する必要がありますか?

これらすべてが、なぜそもそも「最適化」されたのでしょうか?彼らは、時期尚早の最適化がすべての悪の根源であり、文書化されておらず、一見最適化されたコードを見た場合、通常、おそらく最適化ルールに従わなかったと推測でき、最適化の努力を本当に必要としなかっただけであり、いつもの開発者のhub慢がはじまります。しかし、再び、多分それは今あなたの話しているだけかもしれません。

もしそうなら、どの制限内で許容可能になりますか それが必要な場合、この制限が存在し、物事を改善する余地、または手放すことを決定するためのハードラインが与えられます。

また、目に見えない特性に注意してください。おそらく、このコードの「拡張可能な」バージョンでは、実行時のメモリ容量も増え、実行可能ファイルの静的メモリフットプリントがさらに大きくなります。光沢のあるオブジェクト指向機能には、これらのような直感的でないコストが伴います。これらは、プログラムや実行する環境にとって重要な場合があります。

測定、測定、測定

Googleの今のように、それはすべてデータに関するものです!データでバックアップできる場合は、必要です。

それそれが続くことになるの開発に費やしたすべての$ 1本それほど古くない物語あります少なくともテストで$ 1と少なくともサポート$ 1(本当に、それは多くのだが)。

変更は多くのことに影響します:

  • 新しいビルドを作成する必要があるかもしれません。
  • 新しいユニットテストを作成する必要があります(まったくない場合は間違いなく、バグの表面が増えているため、より拡張性の高いアーキテクチャにはおそらく余地が残されています)。
  • 新しいパフォーマンス・テストを(将来的にはこれは安定したまま確認するために、ボトルネックがどこにあるか確認するために)書く必要があり、これらを行うのは難しいです
  • あなたはそれを文書化する必要があります(そして、より拡張可能であることは詳細のためのより多くの余地を意味します)。
  • あなた(または他の誰か)はQAで広範囲に再テストする必要があります。
  • コードは(ほとんど)バグフリーではないため、サポートする必要があります。

したがって、ここで測定する必要があるのは、ハードウェアリソースの消費(実行速度またはメモリフットプリント)だけでなく、チームのリソース消費でもあります。両方をターゲットの目標を定義し、測定し、説明し、開発に基づいて適応させるために予測する必要があります。

そして、あなたのマネージャーにとって、それは現在の開発計画にそれを適合させることを意味するので、それについて通信し、猛烈なカウボーイ/潜水艦/ブラックオペレーションのコーディングに入らないでください。


一般に...

はい、でも...

誤解しないでください。一般的に、あなたが提案した理由を実行することに賛成します。ただし、長期的なコストに注意する必要があります。

完璧な世界では、それは正しい解決策です。

  • コンピューターのハードウェアは時間の経過とともに向上します
  • コンパイラとランタイムプラットフォームは時間の経過とともに改善され、
  • 完璧に近い、クリーンで、保守可能で読みやすいコードが得られます。

実際には:

  • あなたはそれを悪化させるかもしれません

    あなたはそれを見るためにより多くの眼球を必要とします、そしてあなたがそれを複雑にするほど、より多くの眼球が必要になります。

  • あなたは未来を予測することはできません

    絶対に確実に知る必要はありません。必要な「拡張機能」を古い形式で実装する方が簡単で迅速だったとしても、それ自体を最適化する必要があるとしても。

  • それは、経営者の観点から、直接的な利益なしに莫大なコストを表しています。

プロセスの一部にする

あなたはここでそれはかなり小さな変更であり、いくつかの特定の問題を念頭に置いていると言います。この場合は通常大丈夫だと思いますが、私たちのほとんどは小さな変更の個人的な話、ほとんど外科的ストライキの編集もありますコードの背後にある理由を説明し、あるべきではない何かに触れました。

そのような決定を処理するプロセスがある場合、それらから個人的な優位性を取ります。

  • 物事を正しくテストすれば、物事が壊れているかどうかをすばやく知ることができます。
  • それらを測定すると、改善されたかどうかがわかります。
  • 確認すれば、人々を追い払うかどうかがわかります。

テストカバレッジ、プロファイリング、データ収集は難しい

しかし、もちろん、テストコードとメトリックは、実際のコードで回避しようとしているのと同じ問題に悩まされる可能性があります。正しいことをテストし、将来のために正しいことを行い、正しいことを測定しますか物事?

それでも、一般に、テスト(特定の制限まで)と測定を行うほど、収集するデータが増え、安全になります。悪い類推の時間:運転(または一般的な生活)のように考えてください:車があなたに故障したり、誰かが今日自分の車で運転して自分を殺すことにした場合、あなたは世界で最高のドライバーになることができますスキルが足りないかもしれません。あなたを襲う可能性のある環境的なものが両方あり、人的エラーも重要です。

コードレビューは開発チームの廊下試験です

そして、ここで最後の部分が重要だと思います:コードレビューを行います。ソロにした場合、改善の価値がわかりません。コードレビューは「廊下のテスト」です。バグを検出し、オーバーエンジニアリングやその他のアンチパターンを検出するため、およびコードがチームの能力に合っていることを確認するために、レイモンドのLinusの法則に従ってください。誰もがそれを理解し、維持できなければ、「最高の」コードを持つことは意味がありません。そして、それは不可解な最適化と6層の深いアーキテクチャ設計の両方に当てはまります。

最後に、覚えておいてください:

デバッグは、そもそもプログラムを書くよりも2倍難しいことを誰もが知っています。それで、あなたがそれを書くときにあなたがそうすることができるのと同じくらい賢いなら、どのようにそれをデバッグしますか?- ブライアン・カーニガン


「それが壊れていなければ、修正しないでください」はリファクタリングに反します。何かが機能するかどうか、維持できない場合は変更する必要はありません。
宮本明

@MiyamotoAkira:それは2スピードのものです。破損していないが許容範囲内であり、サポートが表示される可能が低い場合は、潜在的な新しいバグを導入したり、開発時間を費やしたりせずにそのままにしておくことが許容される場合があります。リファクタリングの短期的および長期的なメリットを評価することがすべてです。明確な答えはありません。評価が必要です。
ヘイレム

同意した。私はデフォルトのオプションとしてリファクタリングを見ているので、その文(およびその背後にある哲学)が好きではないと思うのですが、時間がかかりすぎるか、難しいと思われる場合にのみ、それと一緒に行きます。気を付けてください、私は人々が物事を変えないことに燃えています。それは、働くものの、あなたがそれらを維持したり拡張したりするとすぐに明らかに間違った解決策でした。
宮本明

@MiyamotoAkira:短い文章や意見表明ではあまり表現できません。彼らはあなたの顔の中にあり、側で開発されることを意図していると思います。大きなセーフティネットや多くの理由がなくても、コードをできる限り頻繁にレビューし、触れることに私は大きく関わっています。汚れている場合は、掃除します。しかし、同様に、私も数回やけどを負いました。そして、まだ焼けます。それが3度のものでない限り、私はそれほど気にしません、これまでのところ、それは常に長期的な利益のための短期的な火傷でした。
ヘイレム

8

一般に、最初に読みやすさに焦点を合わせ、ずっと後にパフォーマンスに焦点を合わせる必要があります。ほとんどの場合、これらのパフォーマンスの最適化はとにかく無視できますが、メンテナンスコストは膨大になる可能性があります。

確かに、あなたが指摘したように、それらのほとんどはコンパイラーによって最適化されるので、すべての「小さな」ものは明快さを優先して変更されるべきです。

大規模な最適化については、最適化が実際に妥当なパフォーマンスを達成するために重要である可能性があります(これは驚くほど頻繁ではありませんが)。私はあなたの変更を行い、変更の前後にコードをプロファイリングします。新しいコードに重大なパフォーマンスの問題がある場合、最適化されたバージョンにいつでもロールバックできます。そうでない場合は、よりクリーンなコードバージョンをそのまま使用できます。

一度にコードの一部のみを変更し、リファクタリングの各ラウンド後のパフォーマンスへの影響を確認します。


8

それは、コードが最適化された理由と、それを変更した場合の影響と、コードが全体的なパフォーマンスに与える影響に依存します。また、テストの変更をロードする適切な方法があるかどうかにも依存します。

この変更は、プロダクションの前後にプロファイリングを行わず、実稼働環境で見られる負荷と同様の負荷の下で行うのが適切です。つまり、開発者のマシン上でデータの小さなサブセットを使用しないか、1人のユーザーのみがシステムを使用している場合にテストします。

最適化が最近の場合は、開発者と話をして、問題が何であったか、最適化前のアプリケーションの速度がどれほど遅かったのかを正確に知ることができます。これにより、最適化を行う価値があるかどうか、および最適化が必要な条件について多くのことがわかります(たとえば、1年をカバーするレポートは、変更をテストする場合、9月または10月まで遅くならないことがあります) 2月には、遅延がまだ明らかになっていない可能性があり、テストは無効です)。

最適化がかなり古い場合、新しいメソッドはより高速で読みやすくなります。

最終的にこれはあなたの上司への質問です。最適化されたものをリファクタリングし、変更が最終結果に影響を及ぼさず、それが以前の方法と同等または少なくとも許容できる程度に機能することを確認するには時間がかかります。彼は、高リスクのタスクを引き受けてコーディング時間を数分節約するのではなく、他の分野で時間を費やすことを望みます。または、コードを理解するのが難しく、頻繁な介入が必要であり、より良い方法が利用できるようになったことに同意するかもしれません。


6

場合プロファイリングは(それがクリティカルセクションではありません)最適化が不要であることを示しているかさえも(悪い時期尚早の最適化の結果として)悪化し、ランタイムを持って、その後必ず保守が容易で読み取り可能なコードと交換してください

また、適切なテストでコードが同じように動作することを確認してください


5

ビジネスの観点から考えてください。変更の費用はいくらですか?コードをより簡単に拡張または保守できるようにすることで、変更を行うのにどれくらいの時間を必要とし、長期的にはどれだけ節約できますか?その時点で価格タグを添付し、パフォーマンスを低下させることで失われたお金と比較します。パフォーマンスの低下を補うために、サーバーを追加またはアップグレードする必要があるかもしれません。製品が要件を満たさなくなり、販売できなくなる可能性があります。たぶん損失はありません。おそらく、この変更により堅牢性が向上し、他の場所で時間を節約できます。今、あなたの決定を下します。

補足として、場合によっては、フラグメントの両方のバージョンを保持することが可能かもしれません。ランダムな入力値を生成するテストを作成し、他のバージョンで結果を検証できます。「賢い」解決策を使用して、完全に理解可能で明らかに正しい解決策の結果を確認し、それによって新しい解決策が古い解決策と同等であることをある程度保証します(ただし、証拠はありません)。または、逆方向に進んで、冗長コードを使用してトリッキーなコードの結果を確認し、それによってハッキングの背後にある意図を明確に文書化します。


4

基本的に、あなたはリファクタリングが価値あるベンチャーかどうかを尋ねています。これに対する答えは確かにイエスです。

しかし...

...慎重に行う必要があります。リファクタリングするコードには、単体、統合、機能、およびパフォーマンスのテストが必要です。あなたは彼らが本当に必要なすべての機能をテストすることを確信する必要があります。それらを簡単に繰り返し実行する機能が必要です。それができたら、コンポーネントを同等の機能を含む新しいコンポーネントに置き換えることができるはずです。

Martin Fowlerがこのを書いた。


3

正当な理由がない限り、稼働中の製品コードを変更しないでください。「リファクタリング」は、そのリファクタリングなしで仕事をすることができない限り、十分な理由ではありません。たとえあなたがやっていることが難しいコード自体のバグを修正しているとしても、あなたはそれを理解するために時間をかけ、可能な限り小さな変更を加えるべきです。コードが理解しにくい場合、コードを完全に理解することはできません。したがって、変更を加えると、予測できない副作用、つまりバグが発生します。変化が大きいほど、トラブルを引き起こす可能性が高くなります。

これには例外があります。理解できないコードに単体テストの完全なセットがあれば、それをリファクタリングできます。完全な単体テストで理解できないコードを見たことも聞いたこともないので、最初に単体テストを記述し、それらの単体テストが実際にコードがすべきことを表す必要な人々の同意を得て、コードを変更します。私は一度か二度それをやった。それは首の痛みであり、非常に高価ですが、最終的には良い結果をもたらします。


3

比較的簡単なことを理解しにくい方法で実行する短いコードの場合は、拡張コメントや未使用の代替実装などで「迅速な理解」に移行します。

#ifdef READABLE_ALT_IMPLEMENTATION

   double x=0;
   for(double n: summands)
     x += n;
   return x;

#else

   auto subsum = [&](int lb, int rb){
          double x=0;
          while(lb<rb)
            x += summands[lb++];
          return x;
        };
   double x_fin=0;
   for(double nsm: par_eval( subsum
                           , partitions(n_threads, 0, summands.size()) ) )
     x_fin += nsm;
   return x_fin;

#endif

3

答えは、一般性を失うことなく、はいです。コードを読みにくい場合は常に最新のコードを追加し、ほとんどの場合は不良コードを削除します。私は次のプロセスを使用します。

  1. パフォーマンステストとサポートプロファイリング情報を探します。パフォーマンステストがない場合、証拠なしに主張できるものは証拠なしに却下できます。最新のコードが高速であることを確認し、古いコードを削除します。誰かが(自分自身でさえ)主張するなら、どちらが速いかを証明するためにプロファイリングコードを書くように彼らに頼みます。
  2. プロファイリングコードが存在する場合、とにかく最新のコードを記述します。次のような名前を付けます<function>_clean()。次に、コードを不良コードと「レース」します。コードが優れている場合は、古いコードを削除します。
  3. 古いコードのほうが速い場合は、とにかくそこに最新のコードを残してください。これは、他のコードが行うことの良いドキュメントとして機能し、「レース」コードがあるので、2つのパス間のパフォーマンス特性と違いをドキュメント化するためにそれを実行し続けることができます。コードの動作の違いを単体テストすることもできます。重要なことは、現代のコードはいつか「最適化された」コードを打ち負かすことを保証します。その後、不良コードを削除できます。

QED。


3

私が死ぬ前に(ソフトウェアについて)一つのことを世界に教えることができたら、「パフォーマンス対X」は偽のジレンマであると教えます。

リファクタリングは通常、読みやすさと信頼性の恩恵として知られていますが、最適化をサポートすることも簡単にできます。一連のリファクタリングとしてパフォーマンスの改善を処理する場合、キャンプサイトルールを尊重しながら、アプリケーションを高速化することができます。実際には、少なくとも私の意見では、そうすることは倫理的にあなたにかかっています。

たとえば、この質問の著者は、クレイジーなコードに遭遇しました。この人が私のコードを読んでいた場合、彼らは狂った部分が3-4行の長さであることに気付くでしょう。それはそれ自体でメソッド内にあり、メソッド名と説明はメソッドが何をしているかを示します。このメソッドには、外観が疑わしいにも関わらず、クレイジーなコードが正しい答えを得る方法を説明する2〜6行のインラインコメントが含まれます。

このように区画化されているので、このメソッドの実装を自由に交換できます。確かに、それはおそらく私が最初からクレイジーなバージョンを書いた方法です。試してみるか、少なくとも代替案について尋ねてください。ほとんどの場合、素朴な実装は著しく悪いことがわかります(通常、私は2-10倍の改善しか望んでいません)が、コンパイラーとライブラリーは常に変化しています。関数が書かれた?


多くの場合、効率化の主要な鍵は、コードが効率的に実行できる方法で可能な限り多くの作業を行うことです。.NETで私を悩ませるものの1つは、たとえばあるコレクションの一部を別のコレクションにコピーする効率的なメカニズムがないことです。ほとんどのコレクションは、連続したアイテム(全体ではない場合)の大きなグループを配列に格納します。たとえば、50,000アイテムのリストから最後の5,000アイテムをコピーすると、いくつかの一括コピー操作(1つだけではない場合)と他のいくつかの操作に分解されます各ステップは多くても数回しか実行されません。
supercat

残念ながら、このような操作を効率的に実行できる場合でも、5,000反復(および場合によっては45,000!)の「バルキー」ループを実行する必要があります。操作をバルクアレイコピーなどに減らすことができる場合、それらを極端な程度に最適化して、大きな見返りを得ることができます。各ループの反復で数十のステップを実行する必要がある場合、それらのいずれかを特にうまく最適化することは困難です。
supercat

2

触れることはおそらく良い考えではありません。パフォーマンス上の理由でコードがそのように書かれている場合、それを変更すると、以前に解決されたパフォーマンスの問題が戻ってくる可能性があることを意味します。

あなたは場合、より読みやすく、拡張するものを変更することを決定した:あなたは、変更を行う前に、下のベンチマーク古いコードを重い負荷。この奇妙なコードが修正するはずのパフォーマンスの問題を説明する古いドキュメントまたはトラブルチケットを見つけることができればさらに良いでしょう。次に、変更を行った後、パフォーマンステストを再度実行します。それほど変わらない場合、またはまだ許容パラメーター内であれば、おそらく大丈夫です。

システムの他の部分が変更されたときに、このパフォーマンス最適化コードがそのような重度の最適化を必要としなくなることもありますが、厳密なテストなしでそれを特定する方法はありません。


1
私が一緒に仕事をしている人の1人は、ユーザーが月に1回、頻繁にヒットする分野で物事を最適化するのが大好きです。時間がかかり、コードアンドコミットを好み、QAまたは他のダウンストリーム機能を実際にテストできるため、まれに他の問題を引き起こすことはありません。:/公平を期すために、彼は一般的に速く、速く、正確ですが、これらの最低限の「最適化」はチームの残りの部分を難しくし、彼らの永久的な死は良いことです。
デイブ

@DaveE:これらの最適化は実際のパフォーマンスの問題のために適用されていますか?それとも、この開発者は彼ができるという理由だけでそれをしますか?最適化が影響を与えないことがわかっている場合は、より読みやすいコードに安全に置き換えることができますが、それを行うのはシステムの専門家だけを信頼します。
FrustratedWithFormsDesigner

彼ができるからです。通常、彼は実際にいくつかのサイクルを節約しますが、プログラム要素とのユーザーインタラクションが数秒(15〜300秒)かかる場合、「効率」を追求する10分の1秒の実行時間を削ることはばかげています。特に彼の後を追う人々が彼がしたことを理解するためにリアルタイムをとらなければならないとき。これはもともと16年前に構築されたPowerBuilderアプリケーションであるため、物事の起源を考えると、おそらくマインドセットは理解可能ですが、彼は現在の現実に自分のマインドセットを更新することを拒否します。
デイブ

@DaveE:私は、あなたよりもあなたと一緒に働いている人にもっと同意すると思います。正当な理由がないために遅いものを修正することが許可されない場合、私は非常識になります。繰り返し+演算子を使用して文字列をアセンブルするC ++の行、または誰かがフラグを設定し忘れたためにループを開くたびに / dev / urandomを開いて読み取るコードが表示される場合は、修正します。これに夢中になることで、他の人が一度に1マイクロ秒スライドさせるときにスピードを維持することができました。
ザンリンクス

1
まあ、私たちは同意しないことに同意する必要があります。1時間を費やして何かを変更して実行時にわずかな時間を節約する機能は、たまにしか実行されず、他の開発者にとっては頭を悩ませるような形のままにしておくのは正しくありません。これらがアプリのストレスの多い部分で繰り返し実行される機能である場合は、うまくいきます。しかし、それは私が説明しているケースではありません。これは、「UserXが週に1回、わずかに高速化することを実現した」と言う以外の理由がない限り、本当に無償のコードです。それまでの間、私たちはやらなければならない仕事を払っています。
デイブ

2

ここでの問題は、「最適化」と読み取り可能および拡張可能を区別することです。ユーザーが最適化コードと見なすものと、コンパイラーが最適化と見なすものは2つの異なるものです。変更しようとしているコードはまったくボトルネックではないため、コードが「無駄のない」場合でも「最適化」する必要はありません。または、コードが十分に古い場合、コンパイラーによって組み込みに対して最適化が行われ、新しい単純な組み込み構造を古いコードと同等またはより効率的に使用できるようになる場合があります。

また、「無駄のない」読み取り不可能なコードが常に最適化されるとは限りません。

私はかつて賢い/無駄のないコードは良いコードであるという考え方でしたが、時にはコード作成の助けではなく、言語の曖昧なルールを利用して、埋め込み作業ではなくよりも頻繁に噛まれましたコンパイラーは、賢いコードを組み込みハードウェアでまったく使用できないものにするため、賢くしてください。


2

パフォーマンスに妥協することはできないため、最適化されたコードを読み取り可能なコードに置き換えることはありません。また、最適化されたセクションに実装されているロジックを誰もが理解できるように、各セクションで適切なコメントを使用することを選択し、両方の問題を解決します。

したがって、コードは最適化され、適切なコメントを付けると読みやすくなります。

注:適切なコメントを使用して、最適化されたコードを読み取り可能にすることはできますが、読み取り可能なコードを最適化することはできません。


コメントを同期しておくのを忘れてコードを編集しているのは1人だけなので、このアプローチにはうんざりします。突然、後続の各レビューは、実際にはY.をしている間、それはXを実行思考離れて歩いていく
ジョン・Dを

2

単純なコードと最適化されたコードの違いを確認する例を次に示します。https//stackoverflow.com/a/11227902/1396264

答えの終わりに向かって彼は単に置き換えます:

if (data[c] >= 128)
    sum += data[c];

で:

int t = (data[c] - 128) >> 31;
sum += ~t & data[c];

公平を期すために、ifステートメントがどのようなものに置き換えられたのかはわかりませんが、回答者が同じ結果を与えるビット演算をいくつか言っているように(私は彼の言葉を受け入れます)

これは元の時間の4分の1未満で実行されます (11.54秒対2.5秒)


1

ここでの主な質問は、最適化が必要ですか?

その場合、より低速で読みやすいコードに置き換えることはできません。読みやすくするには、コメントなどを追加する必要があります。

コードを最適化する必要がない場合は、(読みやすさに影響する程度まで)最適化しないでください。コードをリファクタリングして、読みやすくすることができます。

ただし、変更を開始する前に、コードの機能とコードを徹底的にテストする方法を正確に把握してください。これにはピーク使用率などが含まれます。テストケースのセットを作成して前後に実行する必要がない場合、リファクタリングを行う時間はありません。


1

これは私が物事を行う方法です。まず、読み取り可能なコードで動作させ、次に最適化します。元のソースを保持し、最適化手順を文書化します。

次に、機能を追加する必要がある場合、読み取り可能なコードに戻り、機能を追加して、文書化した最適化手順に従います。文書化したため、新しい機能を使用してコードを再最適化するのは非常に高速で簡単です。


0

ほとんどの場合、マイクロ最適化は価値がないため、IMHOの可読性は最適化されたコードよりも重要です。

ナンセンスなマイクロ最適化に関する記事:

私たちの多くがそうであるように、printをechoに、++ $ iを$ i ++に、二重引用符を単一引用符に置き換えるなど、意味のないマイクロ最適化に関するブログ記事を読むのはうんざりです。どうして?時間の99.999999%であるため、無関係です。

「print」は実際に何かを返すため、「echo」よりも1つ多くのオペコードを使用します。エコーは印刷よりも高速であると結論付けることができます。しかし、1つのオペコードの費用はかかりません。

WordPressの新規インストールを試みました。スクリプトはラップトップで「バスエラー」で終了する前に停止しますが、オペコードの数はすでに230万を超えていました。十分に言った。


0

最適化は相対的です。例えば:

BOOLメンバーがたくさんいるクラスを考えてみましょう。

// no nitpicking over BOOL vs bool allowed
class Pear {
 ...
 BOOL m_peeled;
 BOOL m_sliced;
 BOOL m_pitted;
 BOOL m_rotten;
 ...
};

BOOLフィールドをビットフィールドに変換したくなるかもしれません:

class Pear {
 ...
 BOOL m_peeled:1;
 BOOL m_sliced:1;
 BOOL m_pitted:1;
 BOOL m_rotten:1;
 ...
};

BOOLはINT(Windowsプラットフォームでは符号付き32ビット整数)としてtypedefされているため、これには16バイトがかかり、1つにパックされます。それは93%の節約です!誰がそれについて不平を言うことができますか?

この仮定:

BOOLはINT(Windowsプラットフォームでは符号付き32ビット整数)としてtypedefされているため、これには16バイトがかかり、1つにパックされます。それは93%の節約です!誰がそれについて不平を言うことができますか?

につながる:

BOOLをシングルビットフィールドに変換すると、3バイトのデータが節約されますが、メンバーに非定数値が割り当てられると、8バイトのコードが必要になります。同様に、値の抽出はより高価になります。

かつては

 push [ebx+01Ch]      ; m_sliced
 call _Something@4    ; Something(m_sliced);

になる

 mov  ecx, [ebx+01Ch] ; load bitfield value
 shl  ecx, 30         ; put bit at top
 sar  ecx, 31         ; move down and sign extend
 push ecx
 call _Something@4    ; Something(m_sliced);

ビットフィールドバージョンは9バイト大きくなっています。

座って計算をしましょう。これらのビットフィールド化されたフィールドのそれぞれに、コード内で6回、書き込み用に3回、読み取り用に3回アクセスするとします。コード成長のコストは約100バイトです。オプティマイザーは一部の操作で既にレジスターにある値を利用できる可能性があり、追加の命令にはレジスターの柔軟性の低下という点で隠れたコストがあるため、正確に102バイトにはなりません。実際の差はもっと大きいかもしれませんが、小さいかもしれませんが、エンベロープの計算では100と呼びましょう。一方、メモリ節約はクラスごとに15バイトでした。したがって、損益分岐点は7です。プログラムがこのクラスのインスタンスを7つ未満作成する場合、コードコストはデータの節約を上回ります。メモリの最適化はメモリの最適化解除でした。

参照資料

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.