このサイトにはすでに多くのパフォーマンスに関する質問がありますが、ほとんどすべてが非常に問題固有でかなり狭いことがわかります。そして、ほぼすべてが早すぎる最適化を避けるためにアドバイスを繰り返します。
仮定しましょう:
ここで私が探しているのは、必要なこと以外にやるべきことが残っていない場合に、クリティカルなアルゴリズムで最後の数パーセントまで絞り出す戦略とトリックです。
理想的には、回答を言語にとらわれないようにし、該当する場合は提案された戦略の欠点を示してください。
私自身の最初の提案とともに返信を追加し、Stack Overflowコミュニティが考えられる他のことを楽しみにしています。
さて、あなたは問題を改善の余地がないと思われるところに定義しています。私の経験では、それはかなりまれです。 93年11月のドブス博士の記事でこれを説明しようとしました。従来のよく設計された自明でないプログラムから明らかな無駄がなく、壁時計時間が48から減少するまで一連の最適化を実行しました。秒から1.1秒になり、ソースコードサイズは4分の1に削減されました。私の診断ツール これは です。変更の順序は次のとおりです。
最初に見つかった問題は、半分以上の時間を占めるリストクラスタ(「イテレータ」および「コンテナクラス」と呼ばれる)の使用でした。それらはかなり単純なコードに置き換えられ、時間が20秒に短縮されました。
現在、最大の時間を費やしているのはリストの作成です。割合としては、以前はそれほど大きくはありませんでしたが、今では大きな問題が取り除かれたためです。私はそれをスピードアップする方法を見つけ、時間は17秒に落ちます。
今では明らかな犯人を見つけるのは難しくなっていますが、私が何かできる小さな問題がいくつかあり、時間が13秒に短縮されます。
今、私は壁にぶつかったようです。サンプルはそれが何をしているのかを正確に教えてくれますが、改善できるものを見つけることができないようです。次に、プログラムの基本設計、トランザクション駆動型の構造、およびプログラムが実行しているすべてのリスト検索が問題の要件によって実際に義務付けられているかどうかを検討します。
その後、再設計に行きました。そこでは、プログラムコードは実際には(プリプロセッサマクロを介して)小規模なソースセットから生成され、プログラマーがかなり予測可能であるとわかっているものをプログラムが常に把握していません。言い換えると、やるべきことのシーケンスを「解釈」せず、「コンパイル」します。
さて、非常に高速になっているため、サンプリングするのが難しいため、10倍の作業を行いますが、次の時間は元のワークロードに基づいています。
さらに診断を行うと、キュー管理に時間を費やしていることがわかります。これらをインライン化すると、時間が7秒に短縮されます。
今、私がやっていた診断印刷は大きな時間のかかった人です。フラッシュ-4秒。
現在、最大の時間を費やしているのは、mallocおよびfreeの呼び出しです。オブジェクトのリサイクル-2.6秒。
サンプリングを続けると、厳密に必要ではない操作-1.1秒がまだあります。
合計高速化係数:43.6
現在、2つのプログラムは同じではありませんが、おもちゃではないソフトウェアでは、このような進行が常に見られます。最初に簡単なものを入手し、次により困難なものを入手します。その後、あなたが得た洞察力は再設計につながり、新たなスピードアップを開始し、再び収益が減少するまで続きます。これが、++i
またはi++
またはfor(;;)
またはwhile(1)
の方が高速かどうか疑問に思うかもしれないポイントです。SOでよく見られる種類の質問です。
追伸プロファイラーを使用しなかった理由が不思議に思われるかもしれません。答えは、これらの「問題」のほぼすべてが関数呼び出しサイトであり、スタックサンプルがピンポイントであることです。プロファイラーは、今日でも、関数全体よりもステートメントと呼び出し命令の方が見つけやすく、修正しやすいという考えにほとんど変わりません。実際にこれを行うためにプロファイラーを作成しましたが、コードが何をしているのかについての真のダウンとダーティな親密さのために、あなたの指をその中に正しく入れることに代わるものはありません。サンプルの数が少ないことは問題ではありません。見つかっている問題はどれも非常に小さいため、見逃されやすいからです。
追加:jerryjvlはいくつかの例を要求しました。これが最初の問題です。少数の別個のコード行で構成され、半分以上の時間がかかります。
/* IF ALL TASKS DONE, SEND ITC_ACKOP, AND DELETE OP */
if (ptop->current_task >= ILST_LENGTH(ptop->tasklist){
. . .
/* FOR EACH OPERATION REQUEST */
for ( ptop = ILST_FIRST(oplist); ptop != NULL; ptop = ILST_NEXT(oplist, ptop)){
. . .
/* GET CURRENT TASK */
ptask = ILST_NTH(ptop->tasklist, ptop->current_task)
これらはリストクラスタILSTを使用していました(リストクラスに似ています)。これらは通常の方法で実装され、「情報の隠蔽」とは、クラスのユーザーが実装方法を気にする必要がないことを意味します。これらの行が(約800行のコードから)書かれたとき、これらが "ボトルネック"である可能性があるという考えは与えられませんでした(そのWordが嫌いです)。これらは単に推奨される方法です。後知恵でと言うのは簡単です、これらは避けるべきでしたが、私の経験ではすべてのパフォーマンスの問題はそのようなものです。一般に、パフォーマンスの問題の発生を避けることを試みるのは良いことです。作成されたものを見つけて修正する方が、「避けなければならない」としても(後知恵で)さらに良いです。私はそれが少し風味を与えることを願っています。
次の2番目の問題は、2行に分かれています。
/* ADD TASK TO TASK LIST */
ILST_APPEND(ptop->tasklist, ptask)
. . .
/* ADD TRANSACTION TO TRANSACTION QUEUE */
ILST_APPEND(trnque, ptrn)
これらは、アイテムを末尾に追加してリストを作成しています。 (修正は、アイテムを配列で収集し、リストをすべて一度に作成することでした。)興味深いのは、これらのステートメントは元の時間の3/48しかコストがかからず(呼び出しスタック上にあった)実際、最初は大きな問題です。しかし、最初の問題を取り除いた後、彼らは時間の3/20を要したため、今では「より大きな魚」になりました。一般に、それはそれがどのように行くかです。
このプロジェクトは、私が支援した実際のプロジェクトから蒸留されたものだと付け加えてもよいでしょう。そのプロジェクトでは、内部ループ内でデータベースアクセスルーチンを呼び出してタスクが終了したかどうかを確認するなど、パフォーマンスの問題は(スピードアップと同様に)より劇的でした。
参照の追加:オリジナルと再設計の両方のソースコードは、1993年のファイル9311.Zip、ファイルslug.ascおよびslug.Zipの www.ddj.com にあります。
EDIT 2011/11/26:現在、 sourceforgeプロジェクト Visual C++のソースコードと、調整方法の詳細な説明が含まれています。上記のシナリオの前半のみを通過し、まったく同じシーケンスには従いませんが、それでも2〜3桁の速度向上が得られます。
提案:
パフォーマンスをこれ以上改善できない場合-代わりに知覚パフォーマンスを改善できるかどうかを確認してください。
FooCalcアルゴリズムを高速化することはできないかもしれませんが、多くの場合、アプリケーションがユーザーにより敏感に見えるようにする方法があります。
いくつかの例:
これらはあなたのプログラムをより速くしませんが、それはあなたがあなたの持っている速度であなたのユーザーを幸せにするかもしれません。
私は人生のほとんどをこの場所で過ごします。大まかなストロークは、プロファイラーを実行して記録することです。
__restrict
を自由に使用して、エイリアスについてコンパイラーに約束します。そして、もう1つやりたいことがあります。
より多くのハードウェアを投入してください!
その他の提案:
I/Oを避ける:I/O(ディスク、ネットワーク、ポートなど)は常に、計算を実行しているコードよりもはるかに遅いため、I/Oを取り除く厳密には必要ありません。
I/Oを前もって移動する:計算に必要なすべてのデータを前もって読み込むため、重要なアルゴリズムのコア内でI/Oの待機が繰り返されることはありません(そして、その結果として、1回のヒットですべてのデータをロードするときにシークが回避されるかもしれないとき、ディスクシークを繰り返します。).
遅延I/O:計算が終わるまで結果を書き出さないでください。結果をデータ構造に保存し、最後にハードワークが完了したらダンプします。
スレッドI/O:大胆な場合は、「I/O up-front」または「Delay I/O」を実際の計算と組み合わせて、ロードを並列スレッドに移動します。より多くのデータを読み込んでいる場合は、既に持っているデータの計算に取り組むことができます。または、次のデータのバッチを計算するときに、最後のバッチの結果を同時に書き出すことができます。
パフォーマンスの問題の多くはデータベースの問題に関連しているため、クエリとストアドプロシージャをチューニングする際に確認する特定の事項を示します。
ほとんどのデータベースではカーソルを避けてください。ループも避けてください。ほとんどの場合、データアクセスは、レコードごとの処理ではなく、セットベースである必要があります。これには、1,000,000レコードを一度に挿入する場合に、単一のレコードストアドプロシージャを再利用しないことが含まれます。
Select *は決して使用せず、実際に必要なフィールドのみを返します。これは、結合フィールドが繰り返され、サーバーとネットワークの両方に不必要な負荷がかかるため、結合がある場合に特に当てはまります。
相関サブクエリの使用は避けてください。結合(可能な場合は派生テーブルへの結合を含む)を使用します(これはMicrosoft SQL Serverにも当てはまりますが、異なるバックエンドを使用する場合はアドバイスをテストしてください)。
インデックス、インデックス、インデックス。データベースに該当する場合は、これらの統計情報を更新してください。
クエリを作成します sargable 。つまり、like句の最初の文字にワイルドカードを使用したり、結合内の関数を使用したり、whereステートメントの左部分としてインデックスを使用したりすることを不可能にすることを避けます。
正しいデータ型を使用してください。文字列データ型を日付データ型に変換してから計算するよりも、日付フィールドで日付計算を行う方が高速です。
あらゆる種類のループをトリガーに入れないでください!
ほとんどのデータベースには、クエリの実行方法を確認する方法があります。 Microsoft SQL Serverでは、これを実行計画と呼びます。最初にそれらをチェックして、問題のある場所を確認します。
最適化する必要があるものを決定するときは、クエリの実行頻度と実行にかかる時間を考慮してください。 1か月に1回だけ実行されるlong_runningクエリの時間を消去するよりも、1日数百万回実行されるクエリの微調整からより多くのパフォーマンスを得ることができます。
ある種のプロファイラーツールを使用して、データベースとの間で実際に送信されているものを確認します。過去に、ストアドプロシージャが高速で、Webページが1回ではなく何度もクエリを要求していることがプロファイリングで判明したため、ページの読み込みが非常に遅くなった理由がわからなかったことが一度あります。
プロファイラーは、誰が誰をブロックしているかを見つけるのにも役立ちます。単独で実行中に迅速に実行される一部のクエリは、他のクエリからのロックのために本当に遅くなる場合があります。
今日の最も重要な制限要因は、制限されたメモリ帯域幅です。帯域幅がコア間で共有されるため、マルチコアはこれをさらに悪化させています。また、キャッシュの実装に費やされる限られたチップ領域もコアとスレッドに分割され、この問題をさらに悪化させます。最後に、異なるキャッシュのコヒーレント性を維持するために必要なチップ間シグナリングも、コア数の増加とともに増加します。これもペナルティを追加します。
これらは、管理する必要がある効果です。コードをマイクロ管理することもあれば、慎重に検討してリファクタリングすることもあります。
多くのコメントがすでにキャッシュフレンドリーなコードに言及しています。これには少なくとも2つの異なるフレーバーがあります。
最初の問題は、具体的には、データアクセスパターンをより規則的にし、ハードウェアプリフェッチャーが効率的に動作できるようにすることに関係しています。データオブジェクトをメモリ内に分散させる動的メモリ割り当てを避けます。リンクリスト、ハッシュ、ツリーの代わりに線形コンテナを使用します。
2番目の問題は、データの再利用の改善に関係しています。利用可能なキャッシュに収まるデータのサブセットで動作するようにアルゴリズムを変更し、キャッシュ内にある間は可能な限りそのデータを再利用します。
データをより緊密にパッキングし、ホットループのキャッシュラインのすべてのデータを使用するようにして、これらのその他の影響を回避し、より多くの有用なデータをフィッティングできるようにしますキャッシュ。
「Googleパースペクティブ」を検討する必要があります。つまり、アプリケーションをほぼ並列化および並行処理する方法を決定する必要があります。これは、理想的にはほぼ線形にスケーリングできるように、さまざまなマシンやネットワークにアプリケーションを分散させることを必然的に意味しますあなたがそれに投げるハードウェアで。
一方、Googleの人々は、たとえば gccのプログラム全体の最適化 のように、使用しているプロジェクト、ツール、インフラストラクチャの問題を解決するために多くの人材とリソースを投入することでも知られています。 Googleの典型的なユースケースシナリオに備えてgcc内部をハッキングするエンジニアの専任チームを用意します。
同様に、アプリケーションのプロファイリングとは、プログラムコードだけでなく、その周囲のすべてのシステムとインフラストラクチャ(ネットワーク、スイッチ、サーバー、RAIDアレイなど)をプロファイリングすることではなく、システムの観点から冗長性と最適化の可能性を特定することを意味します。
私はマイク・ダンレイイの答えが好きですが、実際、それは実例をサポートする素晴らしい答えです。
最初に最も時間がかかるものを見つけ、その理由を理解します
タイムホグの識別プロセスが、アルゴリズムをどこで改良する必要があるかを理解するのに役立ちます。これは、すでに完全に最適化されているはずの問題に対して見つけることができる、言語にとらわれない唯一の回答です。また、速度を求めてアーキテクチャに依存しないことを前提としています。
そのため、アルゴリズムは最適化されますが、実装は最適化されない場合があります。この識別により、アルゴリズムまたは実装のどの部分がどの部分であるかを知ることができます。そのため、最も時間を費やしている方が、レビューの第一候補です。ただし、最後の数%を絞り出すと言うので、最初はまだ調べていない部分の小さい部分も調べてください。
最後に、同じソリューションを実装するためのさまざまな方法、または潜在的に異なるアルゴリズムに関するパフォーマンスの数値を使用した少しの試行錯誤は、時間の浪費者と時間の節約者を特定するのに役立つ洞察をもたらします。
HPH、asoudmove。
分割統治
処理中のデータセットが大きすぎる場合、そのチャンクをループします。コードを正しく作成すれば、実装は簡単になります。モノリシックプログラムをお持ちの場合は、今ではよく知っています。
まず最初に、いくつかの以前の回答で述べたように、パフォーマンスを損なうものを学びます-それはメモリまたはプロセッサまたはネットワークまたはデータベースまたは他のものです。それに応じて...
...それが記憶である場合-「コンピュータプログラミングの芸術」シリーズの1つである、Knuthによってずっと前に書かれた本の1つを見つけてください。ほとんどの場合、並べ替えと検索に関するものです-私の記憶が間違っている場合は、遅いテープデータストレージに対処する方法について彼が話していることを見つける必要があります。 memory/tapeペアをキャッシュ/メインメモリのペア(またはL1/L2キャッシュのペア)にそれぞれ精神的に変換します。彼が説明するすべてのトリックを研究してください-あなたがあなたの問題を解決する何かを見つけたら、専門の研究を行うために専門のコンピューター科学者を雇ってください。メモリの問題が偶然FFT(基数2の蝶を行うときにビット反転インデックスでキャッシュミス)である場合、科学者を雇わないでください-代わりに、勝つか得るまで手動でパスを1つずつ最適化してください行き止まりに。あなたは最後の数パーセントまで絞り出す正しいと言いましたか? fewの場合、実際に勝ちます。
...プロセッサの場合-アセンブリ言語に切り替えます。プロセッサの仕様を調べる-ティックを取得するもの、VLIW、SIMD。関数呼び出しは、ほとんどの場合、置き換え可能な目盛りです。ループ変換-パイプライン、展開を学びます。乗算および除算は、ビットシフトで置換/補間できる場合があります(小さな整数の乗算は、加算で置換できる場合があります)。短いデータでトリックを試してください-運がよければ、64ビットの1つの命令が32ビットの2または16ビットの8または8ビットの8に置き換え可能になる可能性があります。 longer dataも試してください。たとえば、特定のプロセッサでfloatの計算が2倍の計算よりも遅くなる場合があります。三角関数を使用している場合は、事前に計算されたテーブルでそれと戦います。また、精度の低下が許容範囲内にある場合、小さな値のサインはその値に置き換えられる可能性があることに注意してください。
...ネットワークの場合-渡すデータを圧縮することを考えてください。 XML転送をバイナリに置き換えます。研究プロトコル。何らかの方法でデータ損失を処理できる場合は、TCPの代わりにUDPを試してください。
...データベースの場合は、データベースフォーラムにアクセスしてアドバイスを求めてください。メモリ内のデータグリッド、クエリプランの最適化など。
HTH :)
キャッシング!ほとんどすべてを高速化するための(プログラマーの努力による)安価な方法は、プログラムのデータ移動領域にキャッシングアブストラクションレイヤーを追加することです。 I/Oであろうと、単にオブジェクトや構造の受け渡し/作成であろうと。多くの場合、ファクトリクラスやリーダー/ライターにキャッシュを追加するのは簡単です。
キャッシュがあまり役に立たない場合もありますが、キャッシュを全体に追加し、それが役に立たない場合は無効にする簡単な方法です。コードをマイクロ分析することなく、膨大なパフォーマンスを得ることができることがよくあります。
これはすでに別の方法で言われていると思います。ただし、プロセッサを集中的に使用するアルゴリズムを扱う場合は、他のすべてを犠牲にして最も内側のループ内のすべてを単純化する必要があります。
それは一部の人には明白に思えるかもしれませんが、私が使用している言語に関係なく、私が焦点を当てようとするものです。たとえば、ネストされたループを処理していて、コードをレベルダウンする機会を見つけた場合、場合によってはコードを大幅に高速化できます。別の例として、可能な場合は常に浮動小数点変数の代わりに整数を使用し、可能な場合は常に除算の代わりに乗算を使用するなど、考慮すべき小さな点があります。繰り返しになりますが、これらは最も内側のループで考慮すべき事項です。
場合によっては、内側のループ内の整数に対して数学演算を実行し、後で操作できる浮動小数点変数にスケールダウンすることの利点を見つけることができます。これは、あるセクションの速度を犠牲にして別のセクションの速度を改善する例ですが、場合によっては見返りに値する場合もあります。
以前の回答ほど詳細でも複雑でもありませんが、ここに行きます:(これらはより初心者/中級レベルです)
この質問に対して一般的な答えを出すことは非常に困難です。それはあなたの問題の領域と技術的な実装に本当に依存します。かなり言語に依存しない一般的な手法:排除できないコードホットスポットを特定し、アセンブラコードを手動で最適化します。
最後の数%は、CPUとアプリケーションに非常に依存するものです...
リストは続きます...しかし、これらの種類のものは本当に最後の手段です...
X86用にビルドし、適切なパフォーマンスプロファイリングのためにコードに対して Valgrind /Cachegrindを実行します。または、Texas Instrumentsの CCStudio には甘いプロファイラーがあります。そうすれば、どこに焦点を合わせるかが本当にわかります...
Did you know that a CAT6 cable is capable of 10x better shielding off extrenal inteferences than a default Cat5e UTP cable?
オフライン以外のプロジェクトでは、最高のソフトウェアと最高のハードウェアを使用しているが、スルー出力が弱い場合、その細い線はデータを絞り込み、ミリ秒単位ではあるが遅延を与えます...しかし、最後のドロップについて話している場合、それは獲得したいくつかのドロップであり、24時間年中無休で送受信されます。
私は、低帯域幅で待ち時間の長いネットワーク(衛星、リモート、オフショアなど)で動作するクライアント/サーバービジネスシステムの最適化に時間を費やし、かなり反復可能なプロセスで劇的なパフォーマンスの改善を達成することができました。
測定:ネットワークの基礎となる容量とトポロジを理解することから始めます。ビジネスの関連するネットワーク担当者と話し、pingやtracerouteなどの基本的なツールを使用して、通常の運用期間中に各クライアントの場所から(少なくとも)ネットワーク遅延を確立します。次に、問題のある症状を表示する特定のエンドユーザー機能の正確な時間測定を行います。これらの測定値のすべてを、それらの場所、日付、時刻とともに記録します。エンドユーザーの「ネットワークパフォーマンステスト」機能をクライアントアプリケーションに組み込み、パワーユーザーが改善プロセスに参加できるようにすることを検討してください。このように権限を付与すると、パフォーマンスの低いシステムにイライラしているユーザーに対処するときに、huge心理的な影響を与える可能性があります。
Analyze:影響を受ける操作の実行中に送受信されるデータを正確に確立するために利用可能なすべてのロギングメソッドを使用します。理想的には、アプリケーションは、クライアントとサーバーの両方で送受信されるデータをキャプチャできます。これらにタイムスタンプも含まれている場合は、さらに良いでしょう。十分なログが利用できない場合(たとえば、クローズドシステム、または運用環境に変更を展開できない場合)、ネットワークスニファーを使用して、ネットワークレベルで何が起こっているかを本当に理解してください。
キャッシュ:静的またはまれにしか変更されないデータが繰り返し送信されるケースを探し、適切なキャッシング戦略を検討します。典型的な例には、「選択リスト」値または他の「参照エンティティ」が含まれます。これらは、一部のビジネスアプリケーションでは驚くほど大きい場合があります。多くの場合、ユーザーは、頻繁に使用されるユーザーインターフェイス要素の表示からかなりの時間を節約できる場合は特に、頻繁に更新されないデータを更新するためにアプリケーションを再起動または更新する必要があることを受け入れることができます。既に展開されているキャッシング要素の実際の動作を理解してください-多くの一般的なキャッシング方法(HTTP ETagなど)は、一貫性を確保するためにネットワークラウンドトリップを引き続き必要とし、ネットワークレイテンシが高い場合は、それを完全に回避できる場合があります別のキャッシングアプローチ。
Parallelise:厳密にシーケンシャルに発行する必要のない論理的にシーケンシャルトランザクションを探し、それらを並行して発行するようにシステムを作り直します。エンドツーエンドリクエストに固有のネットワーク遅延が〜2sである1つのケースに対処しましたが、これは単一トランザクションでは問題ではありませんでしたが、ユーザーがクライアントアプリケーションの制御を取り戻す前に6回の連続2往復が必要であった場合、それはフラストレーションの大きな原因になりました。これらのトランザクションが実際に独立していることを発見したことにより、トランザクションを並行して実行できるようになり、エンドユーザーの遅延を1回の往復のコストに非常に近づけることができました。
結合:順次要求を順次実行する必要がある場合、それらを1つのより包括的な要求に結合する機会を探します。典型的な例には、新しいエンティティの作成と、それらのエンティティを他の既存のエンティティに関連付けるリクエストが含まれます。
Compress:テキスト形式をバイナリ形式に置き換えるか、実際の圧縮技術を使用して、ペイロードの圧縮を活用する機会を探します。多くの最新の(つまり10年以内の)技術スタックは、これをほぼ透過的にサポートしているため、必ず構成してください。問題が帯域幅ではなく基本的に待ち時間であることが明らかであると思われる圧縮の重大な影響に驚かされることが多く、トランザクションが単一のパケット内に収まるか、そうでなければパケット損失を回避してサイズが大きくなるという事実を発見しましたパフォーマンスへの影響。
リピート:初めに戻って、(同じ場所と時間で)操作を再測定し、改善を実施し、結果を記録して報告します。すべての最適化と同様に、いくつかの問題が解決され、現在支配的な他の問題が明らかになっている可能性があります。
上記の手順では、アプリケーション関連の最適化プロセスに焦点を当てていますが、もちろん、基礎となるネットワーク自体も、アプリケーションをサポートするために最も効率的な方法で構成する必要があります。ビジネスのネットワークスペシャリストに連絡し、キャパシティの改善、QoS、ネットワーク圧縮、または問題に対処するための他の技術を適用できるかどうかを判断します。通常、彼らはあなたのアプリケーションのニーズを理解しないので、あなたが彼らと議論するために(分析ステップの後)装備することが重要であり、あなたが彼らに負担することを求めているコストのビジネスケースを作ることも重要です。誤ったネットワーク構成により、アプリケーションデータが陸上リンクではなく低速衛星リンクを介して送信される場合があります。これは、単に「よく知られていない」TCPポートを使用していたためです。ネットワーク専門家;このような問題を明らかに修正すると、ソフトウェアコードや構成の変更がまったく必要なく、パフォーマンスに劇的な影響を与える可能性があります。
他のすべてに含まれていないので、この回答を追加します。
これは、少なくともC/C++に適用されます。すでに思考変換を行っていない場合でも、パフォーマンスを必要とする関数、特にループ内の変換の監視にコンパイラ警告を追加してテストするのに適している場合があります。
GCC固有:コードに冗長なプラグマを追加することでこれをテストできますが、
#ifdef __GNUC__
# pragma GCC diagnostic Push
# pragma GCC diagnostic error "-Wsign-conversion"
# pragma GCC diagnostic error "-Wdouble-promotion"
# pragma GCC diagnostic error "-Wsign-compare"
# pragma GCC diagnostic error "-Wconversion"
#endif
/* your code */
#ifdef __GNUC__
# pragma GCC diagnostic pop
#endif
このような警告によって引き起こされるコンバージョンを減らすことで、数パーセントの高速化を実現できるケースを見てきました。
場合によっては、誤った変換を防ぐために厳重な警告が含まれたヘッダーがありますが、静かな意図的な変換に多くのキャストを追加することになるため、これはトレードオフです。利益。
言うことは不可能です。コードがどのように見えるかに依存します。コードがすでに存在していると仮定できる場合、単純にそれを見て、そこから最適化する方法を見つけ出すことができます。
キャッシュの局所性の向上、ループの展開、長い依存関係チェーンの排除を試み、命令レベルの並列性を向上させます。可能な場合は、ブランチよりも条件付き移動を優先します。可能な場合はSIMD命令を活用します。
コードの実行内容を理解し、実行中のハードウェアを理解します。その後、コードのパフォーマンスを改善するために何をする必要があるかを判断するのはかなり簡単になります。それは本当に私が考えることができる唯一の本当に一般的なアドバイスです。
それで、「SOにコードを表示し、その特定のコードの最適化のアドバイスを求めてください」。
ここに、私が使用するいくつかの迅速で汚い最適化手法を示します。これは「最初のパス」の最適化だと思います。
時間が費やされている場所を学ぶ時間がかかっているものを正確に見つけます。ファイルIOですか? CPU時間ですか?ネットワークですか?データベースですか?それがボトルネックでない場合、IOに最適化することは無意味です。
環境を知る最適化する場所を知ることは通常、開発環境に依存します。たとえば、VB6では、参照渡しは値渡しよりも遅くなりますが、CおよびC++では、参照渡しは非常に高速です。 Cでは、戻りコードが失敗を示す場合、何かを試し、別のことをするのが合理的です。一方、Dot Netでは、試みる前に有効な条件をチェックするよりも、例外をキャッチするのがはるかに遅くなります。
インデックス頻繁にクエリされるデータベースフィールドにインデックスを作成します。ほとんどの場合、速度とスペースを交換できます。
ルックアップの回避最適化されるループ内で、ルックアップを行う必要がなくなります。ループ外でオフセットおよび/またはインデックスを見つけ、内部でデータを再利用します。
IOの最小化特にネットワーク接続を介して読み取りまたは書き込みを行う必要がある回数を減らす方法で設計してください
Reduce Abstractionsコードが処理しなければならない抽象化の層が多いほど、処理は遅くなります。クリティカルループ内で、抽象化を減らします(たとえば、余分なコードを回避する低レベルのメソッドを明らかにする)
Spawn Threadsユーザーインターフェイスを備えたプロジェクトの場合、新しいタスクを実行するために新しいスレッドを生成すると、アプリケーションfeelの応答性が向上しますが、そうではありません。
前処理通常、速度とスペースを交換できます。計算やその他の集中的な操作がある場合は、クリティカルループに入る前に一部の情報を事前に計算できるかどうかを確認してください。
より良いハードウェアがオプションである場合、間違いなくそのために行きます。さもないと
グーグルの方法は、「キャッシュする..可能な限りディスクに触れない」という選択肢の1つです。
組み込みシステムでの変数サイズの縮小
特定のアーキテクチャで変数のサイズがWordのサイズよりも大きい場合、コードのサイズと速度の両方に大きな影響を与える可能性があります。たとえば、16ビットシステムがあり、long int
変数を非常に頻繁に使用し、後で範囲外にならないことを認識した場合(-32.768 ... 32.767)、short int.
に減らすことを検討してください
私の個人的な経験から、プログラムの準備ができているか、ほとんど準備ができているが、ターゲットハードウェアのプログラムメモリの約110%または120%を占めることがわかっている場合、通常、変数をすばやく正規化することで問題を解決できる頻度が高くなります。
この時点で、アルゴリズムまたはコード自体の一部を最適化すると、イライラするほど無駄になります。
多くの人は、変数を使用する単位の数値を正確に格納する変数を持つという誤りを犯します。たとえば、変数time
は、たとえ50 msのタイムステップだけが関連していても、正確なミリ秒数を格納します。変数が1増分ごとに50ミリ秒を表す場合は、Wordサイズ以下の変数に収まる可能性があります。たとえば、8ビットシステムでは、2つの32ビット変数を単純に追加しても、特にレジスタが少ない場合はかなりの量のコードが生成されますが、8ビットの追加は小さくて高速です。
OpenCLまたは(NVidiaチップの場合)CUDAを使用して、グラフィックプロセッサ(存在する場合)に高度に並列化された浮動小数点演算、特に単精度をオフロードする場合。 GPUのシェーダーには膨大な浮動小数点演算能力があり、CPUよりもはるかに優れています。
値ではなく参照で渡す
OSとフレームワークを微調整します。
やり過ぎに聞こえるかもしれませんが、次のように考えてください。オペレーティングシステムとフレームワークは多くのことを行うように設計されています。アプリケーションは非常に具体的なことのみを行います。アプリケーションが必要とするものをOSに正確に実行させ、フレームワーク(php、.net、Java)がどのように機能するかをアプリケーションに理解させることができれば、ハードウェアをさらに改善できます。
たとえば、FacebookはLinuxで kernel level thingys を変更し、memcachedの動作方法を変更しました(たとえば、memcachedプロキシを記述し、 tcpの代わりにudpを使用 )。
別の例はWindow2008です。 Win2K8には、Xアプリケーション(Webアプリ、サーバーアプリなど)を実行するために必要な基本OSのみをインストールできるバージョンがあります。これにより、OSがプロセスを実行する際のオーバーヘッドが大幅に削減され、パフォーマンスが向上します。
もちろん、最初のステップとして常により多くのハードウェアを投入する必要があります...
データのレイアウトを変更すると役立つ場合があります。 Cでは、配列または構造体から配列の構造体に、またはその逆に切り替えることができます。
そのような包括的ステートメントは不可能です。問題のドメインに依存します。いくつかの可能性:
アプリケーションが100%計算することを明確に指定していないため:
データベースを使用しており、たまたまMicrosoft SQL Serverである場合:
があなたのアプリが純粋に計算している場合、大きな画像を回転させるためのキャッシュ最適化についての私の質問を見ることができます。速度の増加は私を驚かせました。
ロングショットですが、特に問題がイメージングドメインにある場合は、アイデアが得られる可能性があります。 rotating-bitmaps-in-code
もう1つは、可能な限り動的なメモリ割り当てを避けることです。複数の構造体を一度に割り当て、一度に解放します。
それ以外の場合は、最も緊密なループを特定し、データ構造の一部を使用して、疑似ループまたは非ループループでここに投稿します。
テンプレート(C++/D)を使用する言語では、テンプレート引数を使用して定数値の伝播を試すことができます。スイッチを使用して、実際には一定ではない小さな値のセットに対してこれを行うこともできます。
Foo(i, j); // i always in 0-4.
になる
switch(i)
{
case 0: Foo<0>(j); break;
case 1: Foo<1>(j); break;
case 2: Foo<2>(j); break;
case 3: Foo<3>(j); break;
case 4: Foo<4>(j); break;
}
欠点はキャッシュのプレッシャーであるため、これは、値が期間中一定である深部または長時間実行のコールツリーでのゲインにすぎません。