web-dev-qa-db-ja.com

埋め込みC ++:STLを使用するかどうか

私は常に組み込みソフトウェアエンジニアでしたが、通常はOSIスタックのレイヤー3または2に所属していました。私は本当にハードウェアの男ではありません。私は通常、テレコム製品、通常はハンド/携帯電話を常に行ってきました。これは、一般的にARM 7プロセッサーのようなものを意味します。

今、私はより一般的な組み込みの世界、小さなスタートアップにいることに気づきました。そこでは、「それほど強力ではない」プロセッサに移動するかもしれません(主観的なビットがあります)-私はどちらを予測することはできません。

私は組み込みシステムのC++でSTLを使用することについての議論についてかなり読みましたが、明確な答えはありません。移植性については若干の心配があり、コードサイズやランタイムについては少し心配ですが、2つの大きな懸念があります。
1-例外処理。私はまだそれを使用するかどうかわかりません( 埋め込みC++:例外を使用するかどうか を参照)
2-組み込みシステムで発生する可能性のある問題のため、組み込みシステムでの動的メモリ割り当てを強く嫌います。通常、コンパイル時に静的に割り当てられ、固定サイズのバッファーのみを提供するバッファープールがあります(バッファーがない場合、システムリセット)。 STLはもちろん、多くの動的な割り当てを行います。

今、私はSTLを使用するかどうかを決定する必要があります-会社全体で、これまでずっと(非常にコアなソフトウェアになります)。

どちらの方法でジャンプできますか?超安全&C++を構成するものの多くを失い(imo、それは単なる言語定義以上のものです)、後で問題に遭遇するか、または多くの例外処理と多分他のコードを追加する必要がありますか?

Boost で行くように誘惑されますが、1)使用する可能性のあるすべての組み込みプロセッサに移植できるかどうかはわかりません。2)彼らのWebサイトでは、そうではないと言います。組み込みシステム(特に奇妙なように見えるFSM)に対して、その特定の部分を保証/推奨します。 Boostに行くと、後で問題が見つかった場合....

70
Mawg

超安全&C++を構成するものの多くを失い(imo、それは単なる言語定義以上のものです)、後で問題に遭遇するか、または多くの例外処理と多分他のコードを追加する必要がありますか?

私たちはゲームの世界でも同様の議論をしており、人々は両方の側に降りてきます。引用部分について、なぜ「C++を構成するものの多く」を失うことを心配するのですか?実用的でない場合は、使用しないでください。 「C++」であるかどうかは関係ありません。

いくつかのテストを実行します。 STLのメモリ管理を満足させる方法で回避できますか?もしそうなら、努力する価値はありましたか? STLとブーストの多くの問題は、偶然の動的メモリ割り当てを回避するように設計した場合、単純に解決しないように設計されています... STLはあなたが直面している特定の問題を解決しますか?

多くの人々が厳しい環境でSTLに取り組み、それに満足しています。多くの人がそれを避けています。一部の人々は、 完全に新しい標準 を提案しています。正しい答えがあるとは思いません。

33
Dan Olson

他の投稿では、動的メモリ割り当て、例外、およびコードの膨張の可能性に関する重要な問題に対処しています。ただ追加したい:_<algorithm>_を忘れないでください! STLベクトルを使用するか、プレーンなC配列およびポインターを使用するかにかかわらず、sort()binary_search()random_shuffle()、ヒープを構築および管理するための関数を引き続き使用できます。など。これらのルーチンは、ほぼ確実に、自分でビルドしたバージョンよりも高速でバグが少ないでしょう。

例:慎重に考えない限り、自分で作成するシャッフルアルゴリズム 歪んだ分布を生成する可能性が高い ; random_shuffle()はしません。

25
j_random_hacker

Electronic Artsは、STLが組み込みコンソール開発に不適切だった理由と、独自の記述をしなければならなかった理由について 長文の論文 を書きました。詳細な記事ですが、最も重要な理由は次のとおりです。

  1. STLアロケーターは遅く、肥大化し、非効率的です
  2. コンパイラーは、実際には、これらすべての深い関数呼び出しをインライン化するのにはあまり適していません
  3. STLアロケーターは明示的なアライメントをサポートしていません
  4. GCCおよびMSVCのSTLに付属するSTLアルゴリズムは、プラットフォームに依存しないため、大きな違いを生む可能性がある多くのマイクロ最適化を見逃しているため、あまりパフォーマンスが高くありません。

数年前、当社はSTLをまったく使用しないことを決定しました。代わりに、最大限のパフォーマンス、デバッグの容易さ、およびより保守的なメモリの独自のシステムを実装しました。大変な仕事でしたが、何度も返済されました。しかし、私たちのものは、与えられたCPUとメモリサイズで製品が16.6msに詰め込むことができる量で競争するスペースです。

例外に関して: それらは遅い コンソールで、そうでなければあなたに言う人はだれでもそれらを計ることを試みなかった。それらを有効にして単純にコンパイルすると、必要なプロローグ/エピローグコードのためにプログラム全体の速度が低下します。信じられない場合は自分で測定してください。順序付けられたCPUでは、x86よりもさらに悪化します。このため、使用しているコンパイラはC++例外もサポートしていません。

パフォーマンスの向上は、例外のスローのコストを回避することではなく、例外を完全に無効にすることによるものです。

19
Crashworks

私はここ数年、組み込み作業を行っておらず、C++を使用したことがないということから始めましょう。ですから、私のアドバイスは、あなたがそれに支払っているすべての額に値します。

STLで使用されるテンプレートは、自分で生成する必要のないコードを生成することは決してないので、コードが肥大化する心配はありません。

STLはそれ自体で例外をスローしないため、心配する必要はありません。クラスがスローしない場合は、安全でなければなりません。オブジェクトの初期化を2つの部分に分割し、コンストラクターにベアボーンオブジェクトを作成させてから、エラーコードを返すメンバー関数で失敗する可能性のある初期化を行います。

すべてのコンテナクラスを使用して独自の割り当て関数を定義できるため、プールから割り当てたい場合は、それを実現できます。

15
Mark Ransom

オープンソースプロジェクト "Embedded Template Library(ETL)" は、ライブラリを提供/実装することにより、組み込みアプリケーションで使用されるSTLの通常の問題を対象としています。

  • 決定論的な動作
  • 「サイズまたは最大サイズがコンパイル時に決定されるコンテナーのセットを作成します。これらのコンテナーは、互換性のあるAPIを使用して、STLで提供されるものとほぼ同等である必要があります。」
  • 動的メモリ割り当てなし
  • rTTIは不要
  • 仮想機能をほとんど使用しない(絶対に必要な場合のみ)
  • 固定容量コンテナのセット
  • 連続的に割り当てられたメモリブロックとしてのコンテナのキャッシュフレンドリーストレージ
  • コンテナコードサイズの削減
  • 型安全なスマート列挙
  • CRC計算
  • チェックサムとハッシュ関数
  • バリアント=タイプセーフなユニオンの種類
  • アサート、例外、エラーハンドラーの選択、またはエラーのチェックなし
  • 徹底的にユニットテスト済み
  • よく文書化されたソースコード
  • その他の機能...

E.S.R.が提供する商用 組み込み開発者向けC++ STL を検討することもできます。ラボ.

5
thinwybk
  1. メモリ管理のために、プールからメモリを要求する独自のアロケータを実装できます。また、すべてのSTLコンテナにはアロケーターのテンプレートがあります。

  2. 例外として、STLは多くの例外をスローしません。一般的に最も一般的なのは、メモリ不足です。あなたの場合、システムをリセットする必要があるため、アロケーターでリセットできます。その他は範囲外などです。ユーザーはそれを回避できます。

  3. だから、組み込みシステムでSTLを使用できると思います:)

5
ddh

基本的には、コンパイラと使用するメモリの量に依存します。 RAMのKbが数個以上ある場合は、動的なメモリ割り当てが役立ちます。標準ライブラリのmallocの実装がメモリサイズに合わせて調整されていない場合は、次のように記述できます。あなた自身のもの、または Ralph Hempelのmm_malloc のような素敵な例があります。これを使用して、新しい演算子を作成したり、演算子を削除したりできます。

例外やstlコンテナが遅すぎる、または肥大化しすぎているなどのミームを繰り返すものには同意しません。もちろん、単純なCのmallocよりも少し多くのコードが追加されますが、例外を賢明に使用するとコードが非常に明確になり、 Cで文句を確認する際のエラーを回避します。

STLアロケーターは2の累乗で割り当てを増やすことに注意する必要があります。つまり、正しいサイズに達するまで再割り当てを行うことがあります。これはreserveとにかく割り当てるサイズがわかれば、希望するサイズの1つのmallocと同じくらい安くなります。

たとえば、ベクターに大きなバッファがある場合、ある時点で再割り当てを行い、データの再割り当てと移動中にある時点で使用する予定のメモリサイズの1.5倍を使用してしまう可能性があります。 (たとえば、ある時点でNバイトが割り当てられ、追加または挿入反復子を介してデータを追加し、2Nバイトを割り当て、最初のNをコピーし、Nを解放します。ある時点で3Nバイトが割り当てられます)。

そのため、最終的には多くの利点があり、自分が何をしているのかを知っていればそれが役に立ちます。組み込みプロジェクトでC++を使用する際にC++がどのように動作するかを少し知っておく必要があります。

そして、固定バッファとリセットの担当者は、新しい演算子またはメモリ不足の場合はいつでもリセットできますが、それはメモリを使い果たす可能性のある悪い設計をしたことを意味します。

ARM realview 3.1でスローされる例外:

--- OSD\#1504 throw fapi_error("OSDHANDLER_BitBlitFill",res);
   S:218E72F0 E1A00000  MOV      r0,r0
   S:218E72F4 E58D0004  STR      r0,[sp,#4]
   S:218E72F8 E1A02000  MOV      r2,r0
   S:218E72FC E24F109C  ADR      r1,{pc}-0x94 ; 0x218e7268
   S:218E7300 E28D0010  ADD      r0,sp,#0x10
   S:218E7304 FA0621E3  BLX      _ZNSsC1EPKcRKSaIcE       <0x21a6fa98>
   S:218E7308 E1A0B000  MOV      r11,r0
   S:218E730C E1A0200A  MOV      r2,r10
   S:218E7310 E1A01000  MOV      r1,r0
   S:218E7314 E28D0014  ADD      r0,sp,#0x14
   S:218E7318 EB05C35F  BL       fapi_error::fapi_error   <0x21a5809c>
   S:218E731C E3A00008  MOV      r0,#8
   S:218E7320 FA056C58  BLX      __cxa_allocate_exception <0x21a42488>
   S:218E7324 E58D0008  STR      r0,[sp,#8]
   S:218E7328 E28D1014  ADD      r1,sp,#0x14
   S:218E732C EB05C340  BL       _ZN10fapi_errorC1ERKS_   <0x21a58034>
   S:218E7330 E58D0008  STR      r0,[sp,#8]
   S:218E7334 E28D0014  ADD      r0,sp,#0x14
   S:218E7338 EB05C36E  BL       _ZN10fapi_errorD1Ev      <0x21a580f8>
   S:218E733C E51F2F98  LDR      r2,0x218e63ac            <OSD\#1126>
   S:218E7340 E51F1F98  LDR      r1,0x218e63b0            <OSD\#1126>
   S:218E7344 E59D0008  LDR      r0,[sp,#8]
   S:218E7348 FB056D05  BLX      __cxa_throw              <0x21a42766>

それほど怖くはないようで、例外がスローされない場合、{}ブロックまたは関数内にオーバーヘッドは追加されません。

3
piotr

すべてのコメントに加えて、 C++パフォーマンスに関するテクニカルレポート を読むことを提案します。これは、特に関心のあるトピックに対処します。例外処理が通常どのように実装され、どのオーバーヘッドが発生するか。無料のストア割り当てのオーバーヘッド。

このレポートは、C++のパフォーマンスに関する多くの一般的なテールを明らかにしているだけでなく、非常に優れています。

3

組み込みシステムでのSTLの最大の問題は、メモリ割り当ての問題です(おっしゃるように、これは多くの問題を引き起こします)。

New/delete演算子をオーバーライドすることで構築された、独自のメモリ管理の作成を真剣に研究しています。私は少しの時間でそれができると確信しています、そしてそれはほぼ間違いなく価値があります。

例外問題に関しては、私はそこに行きません。例外は、コードの深刻なスローダウンです。これは、すべての単一ブロック({ })前後にコードを持ち、例外をキャッチし、その中に含まれるオブジェクトを破棄できるようにします。これに関するハードデータはありませんが、この問題が発生するたびに、例外を使用したことによる大幅な減速の圧倒的な証拠を見てきました。

編集:
多くの人が例外処理がnot遅いというコメントを書いたので、この小さなメモを追加したいと思いました(これをコメントで書いた人に感謝しました。ここに追加してください)。

例外処理によりコードが遅くなる理由は、コンパイラがすべてのブロック({})、例外が処理される場所に例外がスローされる場所から、その中のオブジェクトの割り当てを解除する必要があります。これは、例外がスローされるかどうかに関係なく、すべてのブロックに追加されるコードです(コンパイラーはコンパイル時にこのブロックが例外「チェーン」の一部であるかどうかを判断できないため)。

もちろん、これは、新しいコンパイラーでははるかに高速になった古い方法です(C++コンパイラーの最適化については正確に最新ではありません)。知るための最良の方法は、いくつかのサンプルコードを実行することです。ただし、例外はオンとオフ(およびいくつかのネストされた関数を含む)をオンにして、違いを計ります。

1
Edan Maor

組み込みスキャナープロジェクトでは、ARM7 CPUを搭載したボードを開発していましたが、STLは問題を引き起こしませんでした。動的メモリ割り当ては、現在利用可能な多くのボードやプロジェクトの種類にとって問題にならない可能性があるため、プロジェクトの詳細は重要です。

1
mEbert