web-dev-qa-db-ja.com

マイクロコントローラー用の効率的なCコードの作成に関するリソース?

ここには深刻な助けが必要です。私はプログラミングが大好きです。最近、K言語の本やK/Rなどの記事やフォーラムをC言語でオンラインで読んでいます。 Linuxコードを調べてみました(ただし、どこから始めればよいかわからなくなったのですが、小さなライブラリをのぞくことができましたか?)。

私はJavaプログラマーとして始め、Javaはかなり簡単で乾燥しています。プログラムが大きくなりすぎた場合は、クラスでスライスしてから関数に分けてください。ガイドラインのような、コードを読みやすくし、コメントを追加します。情報の非表示とOOPテクニックを使用します。これらの一部はまだCに適用されます。

私は今Cでコーディングしており、これまでのところ、プログラムを何らかの方法で機能させることができます。多くの人がパフォーマンス/効率、アルゴリズム/設計、最適化、保守性について話します。一部の人は次々にストレスをかけますが、非専門家のソフトウェアエンジニアの場合、たとえばLinuxカーネル開発者はコードをまったく受け取らないということをよく耳にします。

私の質問はこれです:8ビットマイクロコントローラーのコードを書く予定です リソースを無駄にすることなく。 Javaバックグラウンドから来ているので、物事は同じではなくなります...リソース/ブック/リンク/ヒントは大いに評価されます。パフォーマンスとサイズが重要になります。リソース/トリック効率的(ベストプラクティス内)8ビットマイクロコントローラー用のCコード?

また、inline Assemblyは、マイクロコントローラの標準に近づくだけでなく、重要な役割を果たします。しかし、すべてに適用される効率の一般的な経験則はありますか?

例えば: register unsigned int variable_name;はいつでもcharよりも優先されます。またはuint8_t大きな数が必要ない場合。

編集:すべての回答と提案をありがとうございました。知識を共有するための皆さんの努力に感謝します。

15
AceofSpades

私には20年以上の組み込みシステムがあり、ほとんどが8マイクロと16マイクロです。あなたの質問への短い答えは、他のソフトウェア開発と同じです-必要があることがわかるまで最適化せず、最適化する必要があることがわかるまで最適化しないでください。信頼性が高く、読みやすく、保守可能なコードを最初に記述します。早期最適化は、組み込みシステムの問題と同じかそれ以上ではありません

「リソースを無駄にすることなく」プログラムするとき、時間をリソースと見なしますか?そうでない場合は、誰があなたの時間を支払っているか、誰もいない場合は、それを使って何か良いことはありますか?組み込みシステムの設計者が選択しなければならないのは、ハードウェアのコストとエンジニアリング時間のコストです。 100ユニットを出荷する場合、100,000ユニットのより大きなマイクロを使用すると、1ユニットあたり1.00ドルの節約は、ソフトウェア開発の1人年と同じです(市場投入までの時間、機会費用などを無視して)、100万ユニットで開始します。リソースの使用に夢中になるためのROIを取得しますが、多くの組み込みプロジェクトは100万個を販売するように設計されていたため(初期投資が高く、生産コストが低い)、100万個を達成できなかったため、注意が必要です。

とは言っても、(小さな)組み込みシステムで考慮する必要があることは、予期しない方法で動作を停止するだけでなく、単に遅くなるだけではありません。

a)スタック-通常、スタックサイズは小さく、スタックフレームサイズは限られています。スタックの使用率を常に把握しておく必要があります。注意してください、スタックの問題は最も陰湿な欠陥のいくつかを引き起こします。

b)ヒープ-繰り返しになりますが、ヒープサイズが小さいため、不当なメモリ割り当てに注意してください。断片化が問題になります。これら2つについては、実行時に何をするかを知る必要があります。OSが提供するページングのため、大規模なシステムでは発生しません。つまり、mallocがNULLを返す場合、それを確認し、何をしますか。すべてのアオイはチェックとハンドラー、コード膨張を必要としますか?ガイドとして-代替案がある場合は使用しないでください。これらの理由により、ほとんどの小規模なシステムは動的メモリを使用しません。

c)ハードウェア割り込み-安全かつタイムリーな方法でこれらを処理する方法を知る必要があります。安全な再入可能コードを作成する方法も知っておく必要があります。たとえば、C標準ライブラリは一般的に再入可能ではないため、割り込みハンドラー内で使用しないでください。

d)組み立て-ほとんどの場合、時期尚早の最適化。 Cができないことを達成するには、せいぜい少量(インライン化)が必要です。演習として、手作りのアセンブリで小さなメソッドを(ゼロから)作成します。 Cでも同じことを行います。パフォーマンスを測定します。 Cの方が速くなるに違いない。読みやすく、保守しやすく、拡張可能になるだろう。では、演習のパート2として、AssemblyおよびCで有用なプログラムを作成します。
別の演習として、Linuxカーネルのどれだけがアセンブラーであるかを見てください。その後、Linuxカーネルに関する以下の段落をお読みください。

それを行う方法を知ることは価値があり、1つまたは2つの一般的なマイクロの言語に精通している価値さえあるかもしれません。

e)「register unsigned int variable_name」、「register」は、70年代前半(40年前)のコンパイラーへのヒントであり、命令ではありませんでした。 2012年には、コンパイラーは非常にスマートで、micros命令セットは非常に複雑であるため、キーストロークの無駄遣いです。

Linuxのコメントに戻ります。ここでの問題は、100万ユニットを話しているのではなく、1億から数百万を話していることです。人間が可能な限り最適化するためのエンジニアリング時間とコストは、価値があります。非常に優れたエンジニアリング実践の良い例ですが、ほとんどの組み込みシステム開発者がLinuxカーネルが必要とするのと同じぐらい平凡であることは商業的自殺でしょう。

33
mattnz

あなたの質問(「リソースを無駄にすることなく」)は一般的すぎるので、多くのアドバイスをすることは困難です。文字通り、リソースを無駄にしたくない場合は、少し後戻りして、何かをする必要があるかどうか、つまり、他の方法で問題を解決できるかどうかを評価する必要があります。

また、有用なアドバイスは制約によって大きく異なります。どのようなシステムを構築していて、どのようなCPUを使用していますか?ハードリアルタイムシステムですか?コードとデータ用にどのくらいのメモリがありますか?すべてのC演算(特に、乗算と除算)をネイティブにサポートしていますか?より一般的には、データシート全体を読み、理解にしてください。

最も重要なアドバイス:シンプルにしてください。

例:複雑なデータ構造(ハッシュ、ツリー、場合によってはリンクリスト)を忘れて、固定サイズの配列を使用します。より複雑なデータ構造の使用が保証されるのは、配列が遅すぎることを測定によって証明した場合のみです。

また、過度に設計しないでください(Java/C#開発者が行う傾向がある何か):階層化しすぎずに、直接的な手続き型コードを記述します。抽象化にはコストがかかります!

グローバル変数とgotoを使用するという考え方に慣れてください[例外がない場合のクリーンアップに非常に役立ちます];)

割り込みに対処する必要がある場合は、再入可能性についてお読みください。再入可能なコードを書くことは非常に重要です。

3
zvrba

mattnz's answer -に大部分同意します。私は30年以上前に8085でプログラミングを開始し、次にZ80を使用して、すぐに8031に移行しました。その後、68300シリーズマイクロコントローラー、次に80x86、XScale、MXL、および(最新の)8ビットPICSに移行しました。推測は私が一周したことを意味します。ちなみに、いくつかの主要なマイクロプロセッサメーカーのFAEは、意図的なコードの再利用のためにオブジェクト指向の方法ではありますが、依然としてアセンブラを使用していると言えます。

承認された回答に私が見当たらないのは、ターゲットプロセッサの種類や提案されたアーキテクチャの説明です。それはメモリが限られている0.50 $ 8ビターですか?パイプラインと8Mのフラッシュを備えたARM9コアですか?メモリ管理コプロセッサ? OSはありますか? while(1)ループ? 100000ユニットの初期生産稼働を備えた消費者向けデバイス?大きなアイデアと夢を持つ新興企業?

最近のコンパイラーが最適化の素晴らしい仕事をすることに同意しますが、デバッガーを停止せずに生成されたアセンブリコードを見て30年間プロジェクトに取り組んだことがなく、内部で何が起こっているのかを確認しました(パイプライン化と最適化が関係するときの悪夢は確かに)、アセンブリの知識は重要です。

そして、CEO、エンジニアリング担当副社長、ガロンをクオートコンテナーに詰め込んだり、ソフトウェアソリューションを使用してハードウェアの問題を修正することで.05 $節約したりしなかったお客様(これは単なるソフトウェアです)右?何がそんなに難しい?)。メモリ(コードまたはデータ)の最適化は常に考慮されます。

私のポイントは、純粋なプログラミングの観点からプロジェクトを見ると、より狭い範囲のソリューションを賢く取得することになるでしょう。 Mattnzには問題はありません。それを機能させ、より速く、より小さく、より良く機能させますが、コーディングについて考える前に、要件と成果物に多くの時間を費やす必要があります。

3
Gio

いくつか問題があります。最初に、このプロジェクト/コードを移植可能にしますか?移植性はパフォーマンスとサイズを犠牲にしますが、選択したプラットフォームと実装するタスクは、余分なサイズとパフォーマンスの低下を許容できますか?

はい、8ビットマシンでは、unsigned intまたはshortの代わりにunsigned charsを返すことが、パフォーマンスとサイズを改善する1つの方法です。同様に、16ビットマシンでは、unsigned shortと32ビットマシンunsigned intを使用します。たとえば、システム全体で移植性を確保するためにどこでもunsigned intを使用した場合(たとえば、ARMが最小電力、最小のデバイス市場に進入する場合)、そのコードは巨大なrom hogであることが簡単にわかります8ビットのマイクロ。もちろん、int、short、charなしでunsignedを使用して、コンパイラーに最適なサイズを選択させることもできます。

インラインアセンブリだけでなく、アセンブリ言語全般。インラインアセンブリは非常に移植性がなく、asm関数を呼び出すよりも上手に書くことが困難です。はい、あなたはコールのセットアップとリターンを焼きますが、開発がより簡単になり、メンテナンスと制御がより良くなります。ルールは引き続き適用されますが、本当に必要な場合にのみasmで記述します。コンパイラーの出力がこの領域での問題であると結論付けるための作業を行い、手動で行うことでパフォーマンスがどの程度向上するかを確認しました。次に、移植性と保守に戻ります。Cとasmの混合を開始するとすぐに、Cとasmを混合するたびに移植性が損なわれる可能性があり、他の人が作業している場合や、これに関係なくプロジェクトの保守性が低下する可能性があります。現在開発中の製品であり、他の誰かが今後も維持する必要があります。その分析を完了すると、インラインに進む必要があるか、ストレートアセンブリに進む必要があるかどうかが自動的にわかります。私はこの分野で25年以上の経験があり、毎日Cとasmの混合物を書き、ハードウェア/ソフトウェアレイヤーに住んでいますが、インラインasmは使用していません。それは努力に値することはめったになく、あまりにもコンパイラー固有です。可能な限り(ほとんどどこでも)コンパイラー非固有のコードを書きます。

質問全体の鍵は、Cコードを逆アセンブルすることです。コンパイラーがコードをどのように処理するかを学びます。必要に応じて、コンパイラーを操作して、asmに頼る必要なく、必要なコードを生成する方法を学ぶことができます。より多くの時間があれば、コンパイラを操作して複数のターゲットにまたがる効率的なコードを生成し、asmに頼る必要なくコードをよりポータブルにする方法を学ぶことができます。 unsigned charが関数からのステータスリターンとして8ビットマイクロでのunsigned inよりもうまく機能する理由を確認しても問題はありません。同様に、unsigned charは16および32ビットシステムでよりコストがかかります(一部のアーキテクチャでは、外、いくつかはいけない)。 llvmコンパイラシステムでは、1つのプログラムをコンパイルしてから、(gccの場合のように)いくつかの異なるクロスコンパイラを使用しなくても、複数のターゲットのバックエンド出力を調べることができるため、この教育をより迅速に行うことができます。

8ビットマイクロコントローラーの中には、コンパイラーに非常に不親切なものもあり、優れたコードを生成するコンパイラーはありません。それらのデバイスがそれらのターゲットのための優れたコンパイラを作成するためのコンパイラ市場を作成するのに十分な需要はないので、そこにあるコンパイラは、より多くのビジネス、非asmプログラマを引き付けるためにあり、コンパイラは手書きのasmよりも優れているからではありません。 armとmipsがこの世界に参入すると、多くの作業が行われたコンパイラー、かなり良いコードを生成するコンパイラーなどを備えたターゲットがあるため、モデルが変わります。 asmにドロップダウンする必要があるが、それほど頻繁ではない場合、コンパイラーに使用したいことをコンパイラーに伝える方が、使用しないよりもはるかに簡単です。コンパイラを操作することは、見苦しくて判読できないコードではないことに注意してください。実際、それは反対です。ニースのクリーンで単純なコードで、おそらくいくつかのアイテムを再配置します。関数のサイズとパラメーターの数を制御することで、コンパイラーの出力に大きな違いが生まれます。コンパイラまたは言語のKISSの機能を避け、KISSを単純な愚かな状態に保ち、多くの場合より優れた高速なコードを生成します。

1
old_timer

Manttzの回答は、「ハードウェアに近い」プログラミングを行う方法に関する最も重要なポイントを非常によく示しています。これが結局のところCの意味です。

ただし、「クラス」という厳密なキーワードはCに存在しないので、追加したいと思います。これは、Cのオブジェクト指向プログラミングの点では、thinkに非常に簡単です。ハードウェア。

あなたはこの答えを検討するかもしれません: OOこの点を説明するCプログラムのOOベストプラクティス

以下は、Cで適切なオブジェクト指向コードを作成するのに役立つリソースです。

a。 Cでのオブジェクト指向プログラミング
b。ここは 人々がアイデアを交換する良い場所です
c。そして、これが 完全な本です

私があなたに提案したいもう一つの良いリソースは:

The Write Great Code Series 。これは2冊の本です。最初の本は、下位レベルの作業における機械の非常に重要な側面をカバーしています。 2つ目の本は、「低レベルを考える-高レベルを書く」です。

1
Dipan Mehta