update.最近は組み込み用CPUにも色々と高度な機能が付くようになったおかげで、よほどそのCPUに熟知している人を除けば、自分でアセンブラを書くよりコンパイラに最適化を任せた方が早くなる事が増えてきたと思います。言う事で、僕が知っている組み込みソフトウェアの最適化の定石やノウハウをまとめていきたいと思います。wikipediaの最適化の項目が結構充実しているので、こちらから目を通すと良いかも知れません。https://ja.wikipedia.org/wiki/最適化_(情報工学)前置きここでは速度の最適化のみを取り扱います。サイズの最適化についてはスコープ外なので悪しからず。基本的にARM向けの記述が多いです。ARMについては以下を見ると良さそうです。ARMの仕様を見てみるアセンブラの出力例は以下のコンパイラがデフォルト設定で出力したものを使っています。一般的に言って、こんなところではないでしょうか。1.性能を測定できる所まで保守性重視で実装するこの段階では保守性を最優先で進めた方が良いです。高凝集・低結合で変化に強いコードを書きましょう。https://ja.wikipedia.org/wiki/結合度https://ja.wikipedia.org/wiki/凝集度2.性能を測定してボトルネックを探し出すまずはボトルネックがどこにあるのかを探します。これをしないと、性能に全く影響しない所を延々と最適化してしまったりします。CPUによっては性能測定のための機能があったりします。ARMだとパフォーマンスモニタが便利です。http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0363fj/Bgbfdfgh.htmlあとはETMなんてのもあります。3.大域最適化を行うボトルネックの修正のためには大きな設計変更が必要かも知れません。この時に保守性の低いコードを書いていたりすると悲惨な目に遭います。4.局所最適化を行う関数単位の局所的な最適化を行います。ここで得られる効果は微々たるものという事が多いのではないでしょうか。最適化テクニック関数をinline化する常套手段ですね。良く言われるのが関数の呼び出しコストです。処理の順番を変えてパイプラインハザードを解消してくれる時もあります。https://ja.wikipedia.org/wiki/パイプライン処理なお、「処理速度のため」と非常に長い関数を書く人も居ますが、inline化で代替できるケースは多いです。単純にconstを付けると逆に遅くなる可能性もあります。さんご指摘ありがとうございました。constもコンパイラへの最適化のヒントとなりパイプラインハザードが無くなる場合があります。有識者にとっては意外でもなかったりするのかもしれません。空間的局所性の高いアクセスには構造体を使う(追記)最近のコンパイラは頭が良いので、構造体を使わずともオフセット付きロード/ストアにしてくれるようです。さん検証して頂きありがとうございました。構造体を使えば、オフセット付きロード/ストア命令によって高速化できる可能性があります。0x40001000とその隣0x40001004を4Byteずつ取ってくる処理を考えます。*bravo;}これのコンパイル結果はこのようになるはずです(レジスタ名はARMのものを使用)。r0にr0とr1の和を入れる構造体を使う場合、コードはこうなると思います。temperature->bravo;}これのコンパイル結果は以下の様になります。r0にr0とr1の和を入れるBravoにオフセット付きロードが使える分、1命令減ります。someTable[select];}以下の様に、配列の中身を初期化用のrodata領域から一度全てスタックにコピーした後、そこから値を取りだすコードが出力されます。(コメントでの指摘どおり、これは自体は言語規格通りの動作でした)。lrこの例ではコンパイラの最適化によって、スタックを介さずに初期化用のrodata領域から直接r0に格納できそうな気もしますが、どうやらやってくれない様です(?)。someTable[select];}こうすれば、スタックへのコピーを介さず直に読みだしてくれます。lrコンパイラ付属のライブラリを使うガリガリに最適化されたライブラリが入っていることが多いので車輪の再発明をせず素直につかってしまいましょう。変なsection指定attributeが付いている事があるので要注意です。僕は昔これで痛い目にあいました。Linkerの出力から見かけないオブジェクトが変な所に配置されてないか確認しましょう。確立されたアルゴリズムを使う「実はこれってただのソートじゃないか」などと気づく事はありませんか。高速に処理できるアルゴリズムが既にあるなら、それを採用しましょう。コンパイラの最適化オプションを変える僕は割と最近までは知らなかったのですが、コンパイラの-O2と言ったものは個別の最適化オプションの組み合わせでできています。参照:https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html#Optimize-Optionsこれを変えるとぐっと高速になるかもしれません。今まで最適化がかかって無かったために隠れていたバグが表面化する事もあります。CPUの仕様書をじっくり読み込む色々書きましたが、結局はこれが一番重要です。パイプラインの段数によってパイプラインハザードをどの程度恐れる必要があるか変わりますし、フォワーディング機能があればデータハザードは気にしなくて良いでしょう。早いコードの書き方がそのものズバリで書いてあったりします。CPUを使う前提で書きます。CPUは32bit演算しかできません。(16bit)のビットマスクを掛けるので、その分遅くなります。スタックの消費量の観点でも、スタックエントリはCPUのレジスタサイズと同じというケースが多く、この場合8bit,16bit変数も32bitでスタックに積まれます。register指定子を使ってしまうこれはコンパイラによる最適化の自由度を下げるので避けた方が良いです。僕の経験からすると、むしろ遅くなる場合が多いです。最初の実装で最適化してしまう一般的に、プログラムの実行時間の80%は20%のコードが占有すると言われています。僕が昔携わった製品では5%のコードが95%の実行時間を使っていました。そのため、せっかく手間暇かけて最適化しても、プロダクトの性能に影響しなかったりします。この段階で行える最適化となると局所最適化ですが、これによって保守性を下げてしまうと大域最適化の障壁になり、目標性能を達成できなかったという事になりかねません。命令数が減ったから早くなったと判断してしまう逆アセンブラにかければ、その関数の命令数が分かります。https://ja.wikipedia.org/wiki/逆アセンブラ確かに命令数が減ったならその分早くなるケースは多いです。パイプラインハザードを考えると、必ずしもそうとは言えません。実行サイクル数を図ってみると逆に増えているケースもあります。大した話ではありません。何か分かりづらい事や、あるいは間違っている点があれば気軽にコメントしてください。requestStockLike38@YankeeDeltaBravo225組み込み系ドナキチPです。upLoginユーザーは見つかりませんでした。