コードの最適化:最適化するための最適な方法
公開: 2022-03-11パフォーマンスの最適化は、コードに対する最大の脅威の1つです。
あなたは考えているかもしれません、それらの人々の別のものではありません。 わかりました。 語源から判断すると、あらゆる種類の最適化は明らかに良いことであるはずなので、当然、あなたはそれを上手にしたいと思っています。
より良い開発者として群衆から離れるだけではありません。 The Daily WTFで「ダン」になるのを避けるだけでなく、コードの最適化が正しいことだと信じているからです。 あなたは自分の仕事に誇りを持っています。
コンピューターのハードウェアはどんどん速くなり、ソフトウェアは簡単に作れますが、あなたがやりたいと思っている単純なことは何でも、Dammitは常に前回よりも時間がかかります。 あなたはこの現象(ちなみに、ヴィルトの法則として知られている)に頭を振って、その傾向に逆らうことを決心します。
それはあなたの高貴ですが、やめてください。
やめて!
プログラミングの経験がどれほどあっても、あなたは自分の目標を妨げるという重大な危険にさらされています。
どうして? バックアップしましょう。
まず、コードの最適化とは何ですか?
多くの場合、それを定義するとき、コードのパフォーマンスを向上させたいと想定します。 コードの最適化とは、プログラムが可能な限り少ないメモリまたはディスクスペースを使用するように、CPU時間またはネットワーク帯域幅を最小化するように、または追加のコアを最大限に活用するように、コードの書き込みまたは書き換えであると言います。
実際には、デフォルトで別の定義を使用することがあります。それは、コードの記述を減らすことです。
しかし、その目標を持って書いている先制的に悪いコードは、誰かの側でとげになる可能性がさらに高くなります。 だれの? あなたのコードを理解しなければならない次の不運な人、それはあなた自身でさえあるかもしれません。 そして、あなたのような賢くて有能な人は、自己破壊を避けることができます。彼らは間違いなく直感的であるように見えますが、あなたの目的を高貴に保ちながら、あなたの手段を再評価してください。
したがって、コードの最適化は少し漠然とした用語です。 それは、コードを最適化できる他の方法のいくつかを検討する前です。これについては、以下で説明します。
ジャクソンの有名なコード最適化ルールを一緒に探求しながら、賢人のアドバイスを聞くことから始めましょう。
- しないでください。
- (専門家のみ!)まだ実行しないでください。
1.やらないでください:完璧主義のチャネリング
かなり恥ずかしいほど極端な例から始めましょう。昔、SQLの素晴らしい、ケーキを食べて食べ過ぎた世界に足を踏み入れていたときからです。 問題は、それからケーキを踏んで、濡れて足の匂いがし始めたので、もう食べたくなかったということでした。
私はちょうど、SQLの素晴らしい、食べ過ぎの世界に足を踏み入れていました。 問題は、それから私はケーキを踏んだ…
待って。 私が今作って説明した比喩のこの自動車事故から戻ってみましょう。
私はイントラネットアプリの研究開発を行っていましたが、いつか私が働いていた中小企業の完全に統合された管理システムになることを望んでいました。 それは彼らのためにすべてを追跡し、彼らの当時のシステムとは異なり、他の開発者が使用していた不安定な自家製のフラットファイルではなく、RDBMSによってバックアップされるため、データが失われることはありません。 白紙の状態だったので、最初からすべてをできるだけスマートにデザインしたかったのです。 このシステムのアイデアは頭の中で花火のように爆発し、私はテーブルの設計を開始しました。CRM、会計モジュール、在庫、購入、CMS、およびプロジェクト管理の連絡先とその多くのコンテキストバリエーションは、間もなくドッグフーディングになります。
最適化のために、開発とパフォーマンスの面ですべてが停止しました。
オブジェクト(テーブル行として表される)は、現実の世界では互いに多くの異なる関係を持つ可能性があり、これらの関係を追跡することでメリットが得られることがわかりました。より多くの情報を保持し、最終的にはあらゆる場所でビジネス分析を自動化できます。 これをエンジニアリングの問題と見なして、システムの柔軟性の最適化のように見えることを行いました。
この時点で、あなたの顔に注意することが重要です。あなたの手のひらがそれを傷つけた場合、私は責任を負いません。 準備? 2つのテーブルを作成しました。relationship relationship_type
relationship
の外部キー参照を持つテーブルです。 relationship
は、データベース全体の任意の2つの行を参照し、それらの間の関係の性質を表すことができます。
ちょっと、あなた。 私はちょうどその柔軟性を最適化したので、非常に気になります。
実際、多すぎます。 今、私は新しい問題を抱えていました。与えられたrelationship_type
は、与えられた行のすべての組み合わせの間では当然意味がありません。 person
がcompany
との関係でemployed by
ていることは理にかなっているかもしれませんが、それは、たとえば2つのdocument
間の関係と意味的に同等になることは決してありません。
OK、問題ありません。 この関係を適用できるテーブルを指定して、 relationship_type
に2つの列を追加するだけです。 ( relationship_type.id
を参照する新しいテーブルにこれらの2つの列を移動してこれを正規化することを考えた場合は、ここでボーナスポイントが得られます。これにより、複数のテーブルのペアに意味的に適用できる関係では、テーブル名が重複しなくなります。結局のところ、テーブル名を変更する必要があり、該当するすべての行で更新するのを忘れた場合、バグが発生する可能性があります。振り返ってみると、少なくともバグは私の頭蓋骨に生息するスパイダーに食べ物を提供していたでしょう。)
ありがたいことに、私はこの道を行き過ぎて行く前に、手がかりの嵐で無意識にノックされました。 目が覚めたとき、私は、多かれ少なかれ、RDBMSの内部外部キー関連のテーブルをそれ自体の上に再実装することに成功したことに気づきました。 通常、私は「私はとてもメタだ」という大げさな宣言をすることで終わる瞬間を楽しんでいますが、残念ながら、これはそれらの1つではありませんでした。 スケーリングに失敗することを忘れてください。この設計の恐ろしい肥大化により、まだシンプルなアプリのバックエンドが作成されました。このアプリのDBにはまだテストデータがほとんど入力されておらず、ほとんど使用できません。
少し戻って、ここで使用されている多くのメトリックのうちの2つを見てみましょう。 1つは柔軟性です。これは私の目標でした。 この場合、本質的にアーキテクチャである私の最適化は時期尚早ではありませんでした。
(最近公開された記事「時期尚早の最適化の呪いを回避する方法」で詳しく説明します。)それでも、私のソリューションは柔軟性が高すぎるために見事に失敗しました。 もう1つのメトリックであるスケーラビリティは、私がまだ検討していなかったものでしたが、少なくとも巻き添え被害で見事に破壊することができました。
そうです、「ああ」
これは、最適化が完全にうまくいかない可能性があることについての私にとって強力な教訓でした。 私の完璧主義は完全に崩壊しました:私の賢さは、私がこれまでに作った中で最も客観的に賢くない解決策の1つを生み出すように私を導きました。
コードではなく、習慣を最適化する
プロトタイプとテストスイートが機能してその正しさを証明する前にリファクタリングする傾向があることに気付いたら、この衝動を他にどこに向けることができるかを検討してください。 数独とメンサは素晴らしいですが、実際にプロジェクトに直接利益をもたらすものの方が良いかもしれません。
- 安全
- 実行時の安定性
- 明瞭さとスタイル
- コーディング効率
- テストの有効性
- プロファイリング
- ツールキット/DE
- DRY(繰り返さないでください)
ただし、注意してください。これらの特定の1つを最適化すると、他の人が犠牲になります。 少なくとも、時間はかかります。
ここで、コードの作成にどれだけの芸術があるかを簡単に確認できます。 上記のいずれかについて、私はそれがどれほど多すぎるか少なすぎるかが間違った選択であると考えられたという話をあなたに話すことができます。 ここで誰が考えているのかも、文脈の重要な部分です。
たとえば、DRYについて:ある仕事で、少なくとも80%冗長なステートメントであるコードベースを継承しました。これは、その作成者が関数をいつどのように作成するかを知らなかったためです。 コードの残りの20%は、紛らわしいほど自己相似でした。
私はそれにいくつかの機能を追加することを任されました。 そのような機能の1つは、実装するすべてのコードで繰り返す必要があり、将来のコードは、新しい機能を利用するために慎重にコピーパスタする必要があります。
明らかに、それは私自身の正気(高い価値)と将来の開発者のためだけにリファクタリングする必要がありました。 しかし、私はコードベースを初めて使用したため、リファクタリングによってリグレッションが発生しないことを確認できるように、最初にテストを作成しました。 実際、彼らはまさにそれを実行しました。スクリプトが生成したすべてのgobbledygook出力の中で、気付かなかった2つのバグを途中で見つけました。
結局、私はかなりうまくやったと思いました。 リファクタリング後、数行の単純なコードで難しい機能と見なされていたものを実装したことで上司に感銘を与えました。 さらに、コードは全体的に桁違いにパフォーマンスが向上しました。 しかし、同じ上司が私が遅すぎた、そしてプロジェクトはすでに終わっているはずだと私に言ったのは、この後それほど長くはありませんでした。 翻訳:コーディング効率がより優先されました。
注意:特定の[側面]から一体を最適化すると、他の人が犠牲になります。 少なくとも、時間はかかります。
当時上司がコードの最適化を直接評価していなかったとしても、私はそこで正しい道を歩んだと思います。 リファクタリングとテストがなければ、実際に正しくなるまでにもっと時間がかかったと思います。つまり、コーディング速度に焦点を合わせると、実際にはそれが妨げられたでしょう。 (ねえ、それが私たちのテーマです!)
これを、私の小さなサイドプロジェクトで行ったいくつかの作業と比較してください。 プロジェクトでは、新しいテンプレートエンジンを試していましたが、新しいテンプレートエンジンを試すことはプロジェクトの最終目標ではありませんでしたが、最初から良い習慣を身に付けたいと思っていました。
追加したいくつかのブロックが互いに非常に類似していることに気付くとすぐに、さらに、各ブロックが同じ変数を3回参照する必要があり、DRYベルが頭の中で鳴り、正しいものを見つけようとしました。このテンプレートエンジンでやろうとしていたことを行う方法。
数時間の無駄なデバッグの後、私が想像した方法では、テンプレートエンジンではこれが現在不可能であることが判明しました。 完璧なDRYソリューションがなかっただけではありません。 DRYソリューションはまったくありませんでした!
私のこの1つの価値を最適化しようとして、コーディングの効率と幸福を完全に狂わせました。これは、この迂回によって、その日のプロジェクトの進捗が失われたためです。
それでも、私は完全に間違っていましたか? 特に新しい技術コンテキストでは、後でではなく早期にベストプラクティスを理解するために、少し投資する価値がある場合があります。 書き直すコードが減り、元に戻す習慣が悪くなりますよね?
いいえ、以前の逸話での私の態度とはまったく対照的に、コードの繰り返しを減らす方法を探すのは賢明ではなかったと思います。 その理由は、コンテキストがすべてであるということです。私は、長期にわたって解決するのではなく、小さな遊びのプロジェクトで新しいテクノロジーを模索していました。 いくつかの余分な行と繰り返しは誰も傷つけませんでしたが、焦点の喪失は私と私のプロジェクトを傷つけました。
待ってください。ベストプラクティスを探すのは悪い習慣になる可能性がありますか? ときどき。 私の主な目標が新しいエンジンを学ぶこと、または一般的に学ぶことだったとしたら、それはよく費やされたでしょう:いじくり回し、限界を見つけ、研究を通して無関係な機能と落とし穴を発見します。 しかし、これが私の主な目標ではないことを忘れていたので、コストがかかりました。
私が言ったように、それは芸術です。 そして、その芸術の発展は、リマインダーから恩恵を受けます、それをしないでください。 少なくとも、作業中にどの値が機能しているか、そしてどの値があなたのコンテキストで最も重要であるかを検討することができます。
その2番目のルールはどうですか? 実際に最適化できるのはいつですか?
2.まだやらないでください:誰かがすでにこれを行っています
OK、あなたか他の誰かによって、あなたはあなたのアーキテクチャがすでに設定されていて、データフローが考えられて文書化されていることに気づき、そしてコーディングする時が来ました。
まだ一歩踏み出さないでください。まだコーディングしないでください。
これ自体は時期尚早の最適化のようなにおいがするかもしれませんが、それは重要な例外です。 なんで? 恐ろしいNIHS、または「ここで発明されていない」症候群を回避するため。優先順位にコードのパフォーマンスと開発時間の最小化が含まれていると仮定します。 そうでない場合、目標が完全に学習指向である場合は、この次のセクションをスキップできます。
人々が純粋な傲慢さから四角い車輪を再発明することは可能ですが、あなたや私のような正直で謙虚な人々は、私たちが利用できるすべてのオプションを知らないだけでこの間違いを犯すことができると信じています。 スタック内のすべてのAPIとツールのすべてのオプションを理解し、それらが成長し進化するにつれてそれらを常に把握することは、確かに多くの作業です。
しかし、この時間を入れることはあなたを専門家にし、CodeSODの無数の人物になって、日時計算機や文字列マニピュレーターの魅力的なテイクによって残された荒廃の痕跡に呪われたり嘲笑されたりするのを防ぎます。
(この一般的なパターンに対する適切な対抗策は、古いJava Calendar
APIですが、その後修正されました。)
標準ライブラリをチェックし、フレームワークのエコシステムをチェックし、問題をすでに解決しているFOSSをチェックします
おそらく、あなたが扱っている概念はかなり標準的でよく知られている名前を持っているので、インターネットですばやく検索すると時間を大幅に節約できます。
例として、私は最近、ボードゲームのAI戦略の分析を行う準備をしていました。 ある朝目が覚めたとき、私が計画していた分析は、覚えている特定の組み合わせ論の概念を使用するだけで、桁違いに効率的に実行できることに気付きました。 現時点では、この概念のアルゴリズムを自分で理解することに興味はありませんでしたが、検索する正しい名前を知っていることで、私はすでに先を行っていました。 しかし、約50分間の調査と予備的なコードの試行の後、見つけた半完成の擬似コードを正しい実装に変えることができなかったことがわかりました。 (著者が誤ったアルゴリズム出力を想定し、その想定に一致するようにアルゴリズムを誤って実装し、コメント投稿者がこれを指摘し、数年後もまだ修正されていないというブログ投稿があると信じられますか?)その時点で、私の朝のお茶キックインして、 [name of concept] [my programming language]
を検索しました。 30秒後、GitHubからコードを修正し、実際にやりたいことを実行していました。 自分で実装する必要があると想定するのではなく、具体的に言語を含めるだけで、すべてが意味します。
データ構造を設計し、アルゴリズムを実装する時間
…繰り返しますが、コードゴルフはしないでください。 実際のプロジェクトでは、正確さと明確さを優先します。

さて、あなたは見てきましたが、ツールチェーンに組み込まれている、またはWeb上で自由にライセンスされている問題を解決するものはまだありません。 あなたはあなた自身を展開します。
問題ない。 アドバイスは次の順序で簡単です。
- 初心者プログラマーに簡単に説明できるように設計してください。
- その設計によって生成された期待に適合するテストを作成します。
- 初心者プログラマーがコードからデザインを簡単に収集できるように、コードを記述します。
単純ですが、おそらく従うのは難しいでしょう。 ここで、コーディングの習慣とコードの臭い、アートとクラフトとエレガンスが作用します。 この時点で行っていることには明らかに工学的な側面がありますが、繰り返しになりますが、コードゴルフはプレーしないでください。 実際のプロジェクトでは、正確さと明確さを優先します。
あなたがビデオが好きなら、これは多かれ少なかれ上記のステップに従っている誰かの一人です。 ビデオ嫌いのために、私は要約します:それはグーグルの就職の面接でのアルゴリズムコーディングテストです。 インタビュー対象者は、最初に、コミュニケーションが容易な方法でアルゴリズムを設計します。 コードを書く前に、実際の設計で期待される出力の例があります。 次に、コードは自然に続きます。
テスト自体に関しては、一部のサークルでは、テスト駆動開発が論争になる可能性があることを私は知っています。 その理由の一部は、それがやり過ぎて、開発時間を犠牲にするまで宗教的に追求される可能性があるからだと思います。 (繰り返しになりますが、最初から1つの変数でも最適化しすぎて、足を踏み入れてしまいます。)ケントベックでさえTDDをそれほど極端にとらえず、極端なプログラミングを発明し、TDDに関する本を書きました。 したがって、出力が正しいことを確認するために、簡単なことから始めてください。 結局のところ、とにかくコーディングした後、手動でそれを行うでしょう? (あなたが最初にコードを書いた後にコードを実行することさえしないようなロックスタープログラマーである場合は、お詫びします。その場合、コードの将来のメンテナにテストを任せることを検討してください。素晴らしい実装を破ります。)したがって、手動で視覚的な差分を行う代わりに、テストを実施して、すでにコンピューターにその作業を任せています。
アルゴリズムとデータ構造を実装するかなり機械的なプロセスでは、行ごとの最適化を行わないようにし、カスタムの低水準言語externを使用することさえ考えないでください(Cでコーディングしている場合はアセンブリ、Cでコーディングしている場合はC 'この時点でPerlなどでコーディングしています。 理由は単純です。アルゴリズムが完全に置き換えられ、プロセスの後半でそれが必要かどうかがわからない場合、低レベルの最適化の取り組みは最終的には効果がありません。
ECMAScriptの例
優れたコミュニティコードレビューサイトexercism.ioで、最近、重複排除または明確化のために最適化を試みることを明示的に提案する演習を見つけました。 重複排除を最適化したのは、DRYを使用した場合に、いかにばかげたことが起こるかを示すためです。これは、前述のように、他の点では有益なコーディングの考え方です。 私のコードは次のようになりました。
const zeroPhrase = "No more"; const wallPhrase = " on the wall"; const standardizeNumber = number => { if (number === 0) { return zeroPhrase; } return '' + number; } const bottlePhrase = number => { const possibleS = (number === 1) ? '' : 's'; return standardizeNumber(number) + " bottle" + possibleS + " of beer"; } export default class Beer { static verse(number) { const nextNumber = (number === 0) ? 99 : (number - 1); const thisBottlePhrase = bottlePhrase(number); const nextBottlePhrase = bottlePhrase(nextNumber); let phrase = thisBottlePhrase + wallPhrase + ", " + thisBottlePhrase.toLowerCase() + ".\n"; if (number === 0) { phrase += "Go to the store and buy some more"; } else { const bottleReference = (number === 1) ? "it" : "one"; phrase += "Take " + bottleReference + " down and pass it around"; } return phrase + ", " + nextBottlePhrase.toLowerCase() + wallPhrase + ".\n"; } static sing(start = 99, end = 0) { return Array.from(Array(start - end + 1).keys()).map(offset => { return this.verse(start - offset); }).join('\n'); } }
そこに文字列の重複はほとんどありません! このように書くことで、私はビールの歌のテキスト圧縮の形式を手動で実装しました(ただし、ビールの歌のみ)。 正確には、どのようなメリットがありましたか? さて、あなたがボトルの代わりに缶からビールを飲むことについて歌いたいとしましょう。 これは、 bottle
の1つのインスタンスをcan
に変更することで実現できます。
良い!
…正しい?
いいえ、すべてのテストが失敗するためです。 OK、それは簡単に修正できます。ユニットテスト仕様でbottle
を検索して交換するだけです。 そして、それはそもそもコード自体にそれを行うのとまったく同じくらい簡単であり、意図せずに物事を壊すという同じリスクを伴います。
その間、私の変数は後で奇妙な名前が付けられ、 bottlePhrase
のようなものはボトルとはまったく関係がありません。 これを回避する唯一の方法は、行われる変更の種類を正確に予測し、変数名のbottle
の代わりにvessel
やcontainer
などのより一般的な用語を使用することです。
このように将来を保証するという知恵はかなり疑わしいものです。 何かを変えたいと思う確率はどれくらいですか? そして、あなたがそうするならば、あなたが変えるものはとても便利にうまくいくでしょうか? bottlePhrase
の例では、複数形が2つ以上ある言語にローカライズする場合はどうでしょうか。 そうです、時間をリファクタリングすると、コードは後でさらに悪化する可能性があります。
しかし、要件が変更され、それらを予測しようとしているだけではない場合は、リファクタリングする時期かもしれません。 または、それでも延期することができます。現実的に、いくつの船種またはローカリゼーションを追加しますか? とにかく、重複排除と明確さのバランスをとる必要がある場合は、カトリーナオーウェンによるこのデモンストレーションを見る価値があります。
私自身の醜い例に戻りましょう。言うまでもなく、重複排除のメリットはここではそれほど実現されていません。 その間、いくらかかりましたか?
そもそも書き込みに時間がかかることを除けば、読み取り、デバッグ、および保守を行うのは今ではかなり簡単ではありません。 適度な量の複製が許可された読みやすさのレベルを想像してみてください。 たとえば、4つの詩のバリエーションのそれぞれを綴ります。
しかし、まだ最適化されていません!
アルゴリズムが実装され、その出力が正しいことが証明されたので、おめでとうございます。 ベースラインがあります!
最後に、最適化する時が来ましたね。 いいえ、まだやらないでください。 それはあなたのベースラインを取り、素晴らしいベンチマークを行う時です。 これに関する期待のしきい値を設定し、テストスイートに貼り付けます。 次に、何かが突然このコードを遅くした場合、たとえそれがまだ機能していても、ドアから出る前にわかります。
関連するユーザーエクスペリエンスの全体が実装されるまで、最適化を保留します。 その時点までは、必要とはまったく異なるコードの部分をターゲットにしている可能性があります。
まだ行っていない場合は、アプリ(またはコンポーネント)を完成させ、すべてのアルゴリズムベンチマークベースラインを設定します。
これが完了したら、システムの最も一般的な実際の使用シナリオをカバーするエンドツーエンドのテストを作成してベンチマークする絶好の機会です。
多分あなたはすべてが大丈夫であることがわかるでしょう。
あるいは、実際の状況では、何かが遅すぎるか、メモリを大量に消費していると判断したかもしれません。
OK、今あなたは最適化することができます
それについて客観的になる唯一の方法があります。 フレームグラフやその他のプロファイリングツールを作成するときが来ました。 経験豊富なエンジニアは、初心者よりも頻繁に推測する場合としない場合がありますが、それは重要ではありません。確実に知る唯一の方法は、プロファイルを作成することです。 これは、パフォーマンスのためにコードを最適化するプロセスで常に最初に行うことです。
特定のエンドツーエンドテスト中にプロファイルを作成して、実際に最大の影響を与えるものを取得できます。 (後で、展開後、使用パターンを監視することは、システムのどの側面が将来測定するのに最も関連性があるかを把握するための優れた方法です。)
プロファイラーを完全に使用しようとしているのではないことに注意してください。通常、この時点での目標はどのアルゴリズムがボトルネックであるかを見つけることだけであるため、ステートメントレベルのプロファイリングよりも関数レベルのプロファイリングを探しています。 。
プロファイリングを使用してシステムのボトルネックを特定したので、実際に最適化を試みることができ、最適化を行う価値があると確信できます。 また、途中で行ったベースラインベンチマークのおかげで、試行がどれほど効果的(または非効果的)であったかを証明することもできます。
全体的なテクニック
まず、可能な限り高レベルを維持することを忘れないでください。
知ってますか? 究極のユニバーサル最適化トリックは、すべての場合に適用されます。
— Lars Doucet(@larsiusprime)2017年3月30日
-描画するものを減らします
-更新するものが少なくなります
アルゴリズム全体のレベルでは、1つの手法は強度の低減です。 ただし、ループを数式に減らす場合は、コメントを残すことに注意してください。 誰もがすべての組み合わせ論の公式を知っている、または覚えているわけではありません。 また、数学の使用には注意してください。最終的には、強度の低下と思われることがそうではない場合があります。 たとえば、 x * (y + z)
が明確なアルゴリズムの意味を持っていると仮定しましょう。 何らかの理由で、ある時点で脳が同類項のグループ化を自動的に解除するように訓練されている場合は、それをx * y + x * z
として書き直したくなるかもしれません。 一つには、これは読者とそこにあった明確なアルゴリズムの意味との間に障壁を置きます。 (さらに悪いことに、余分な乗算操作が必要なため、実際には効率が低下します。これは、ズボンを固めただけのループ展開のようなものです。)いずれにせよ、意図についての簡単なメモは大いに役立ち、コミットする前に独自のエラー。
数式を使用している場合でも、ループベースのアルゴリズムを別のループベースのアルゴリズムに置き換えている場合でも、違いを測定する準備ができています。
ただし、データ構造を変更するだけでパフォーマンスが向上する可能性があります。 使用している構造で実行する必要のあるさまざまな操作のパフォーマンスの違い、およびその他の方法について学習します。 ハッシュはコンテキスト内で機能するのが少し面倒に見えるかもしれませんが、配列よりも優れた検索時間はそれだけの価値がありますか? これらは、決定するのはあなた次第であるトレードオフのタイプです。
これは、コンビニエンス関数を呼び出すときに、どのアルゴリズムが実行されているかを知ることになります。 つまり、最終的には強度低下と同じです。 また、ベンダーのライブラリが舞台裏で何をしているのかを知ることは、パフォーマンスだけでなく、意図しないバグを回避するためにも重要です。
マイクロ最適化
OK、システムの機能は完了しましたが、UXの観点からは、パフォーマンスをもう少し微調整することができます。 あなたがより高いレベルでできることをすべて行ったと仮定して、これまでずっと避けてきた最適化を検討する時が来ました。 このレベルの最適化は、明快さと保守性とのトレードオフであるため、検討してください。 しかし、今がその時だと判断したので、ステートメントレベルのプロファイリングを進めてください。これで、実際に重要なシステム全体のコンテキスト内にいることになります。
使用するライブラリと同様に、コンパイラまたはインタプリタのレベルでの利益のために、数え切れないほどのエンジニアリング時間が費やされています。 (結局のところ、コンパイラの最適化とコード生成は、それ自体が大きなトピックです)。 これは、プロセッサレベルでも当てはまります。 最低レベルで何が起こっているかを意識せずにコードを最適化しようとすることは、四輪駆動を使用することは、車両もより簡単に停止できることを意味すると考えるようなものです。
それはあなたの技術スタックとあなたのプロファイラーが指しているものに本当に依存するので、それを超えて良い一般的なアドバイスを与えるのは難しいです。 しかし、あなたは測定しているので、解決策が問題の文脈から有機的かつ直感的にあなたに提示されない場合、あなたはすでに助けを求めるための優れた立場にいます。 (睡眠と他のことを考えるのに費やした時間も役立ちます。)
この時点で、コンテキストとスケーリング要件に応じて、Jeff Atwoodはおそらく、開発者の時間よりも安価なハードウェアを追加することを提案するでしょう。
多分あなたはそのルートに行かないでしょう。 その場合、コード最適化手法のさまざまなカテゴリを調査すると役立つ場合があります。
- キャッシング
- ビットハックおよび64ビット環境に固有のハック
- ループ最適化
- メモリ階層の最適化
すなわち:
- CおよびC++でのコード最適化のヒント
- Javaでのコード最適化のヒント
- .NETでのCPU使用率の最適化
- ASP.NETWebファームのキャッシュ
- SQLデータベースのチューニングまたは特にMicrosoftSQLServerのチューニング
- Scalaのプレイをスケーリングする! フレームワーク
- 高度なWordPressパフォーマンスの最適化
- JavaScriptプロトタイプとスコープチェーンによるコードの最適化
- Reactパフォーマンスの最適化
- iOSアニメーションの効率
- Androidのパフォーマンスのヒント
いずれにせよ、私はあなたのためにいくつかのより多くの禁止事項を持っています:
複数の異なる目的で変数を再利用しないでください。 保守性という点では、これはオイルなしで車を走らせるようなものです。 これは、最も極端な埋め込み状況でのみ意味があり、そのような場合でも、もはや意味がないと私は主張します。 これは、整理するコンパイラの仕事です。 自分で実行してから、コードを1行移動すると、バグが発生します。 あなたにとってそれだけの価値のあるメモリを節約するという幻想はありますか?
理由を知らずにマクロやインライン関数を使用しないでください。 はい、関数呼び出しのオーバーヘッドはコストです。 ただし、これを回避すると、コードのデバッグが困難になることが多く、実際には遅くなることもあります。 たまに良いアイデアであるという理由だけでこのテクニックをどこでも使用することは、黄金のハンマーの例です。
ループを手動で展開しないでください。 繰り返しになりますが、この形式のループ最適化は、コードの可読性を犠牲にするのではなく、コンパイルなどの自動化されたプロセスによってほとんどの場合より適切に最適化されます。
最後の2つのコード最適化の例の皮肉なことに、実際にはパフォーマンスが低下する可能性があります。 もちろん、ベンチマークを実行しているので、特定のコードに対してそれを証明または反証することができます。 ただし、パフォーマンスの向上が見られたとしても、アート側に戻って、読みやすさと保守性を損なうだけの価値があるかどうかを確認してください。
それはあなた次第です:最適化された最適化
パフォーマンスの最適化を試みることは有益です。 しかし、多くの場合、それは非常に時期尚早に行われ、多くの悪い副作用を伴い、最も皮肉なことに、パフォーマンスの低下につながります。 最適化の芸術と科学、そして最も重要なこととして、その適切なコンテキストに対する理解が深まったことを願っています。
これにより、最初から完璧なコードを書くという概念を捨てて、代わりに正しいコードを書くことができれば幸いです。 トップダウンで最適化し、ボトルネックがどこにあるかを証明し、それらを修正する前後に測定することを忘れないでください。 これが、最適化を最適化するための最適で最適な戦略です。 幸運を祈ります。