次の方法で共有


コーディングの基礎

アプリケーション コードは、このトピックで定義されている最小品質標準を満たしていることをお勧めします。 運用環境にデプロイされたアプリケーションの改善を目指すお客様とのパートナーシップを通じて、一般的な問題がいくつか見つかりました。この問題が修正されると、アプリケーションのパフォーマンスが向上します。

一般的な問題

  • ターゲット API セットを設定する場合は、最新の CMake ツールと Azure Sphere ツールを使用し、 を設定 AZURE_SPHERE_TARGET_API_SET="latest-lts"して最終的なリリース バイナリをコンパイルすることをお勧めします。 詳細については、「 再生可能なセキュリティのコーディング」を参照してください。

メモ

製造プロセス内でサイドロードされるイメージ パッケージを具体的に作成する場合は、デバイスのソースまたは回復先の適切な Azure Sphere OS バージョンに設定 AZURE_SPHERE_TARGET_API_SET します。これを行わないと、Azure Sphere OS によってイメージ パッケージが拒否されます。

  • アプリケーションを運用環境に デプロイする 準備ができたら、リリース モードで最終的なイメージ パッケージをコンパイルしてください。
  • コンパイラの警告にもかかわらず、運用環境にデプロイされたアプリケーションが表示されるのが一般的です。 完全ビルドに対して警告ゼロ ポリシーを適用すると、すべてのコンパイラ警告が意図的に対処されます。 最も頻繁に発生する警告の種類を次に示します。これは、対処することを強くお勧めします。
    • 暗黙的な変換関連の警告: 多くの場合、バグは、最初のクイック実装によって暗黙的な変換が発生したために導入されます。これは、未確認のままでした。 たとえば、異なる数値型間で多くの暗黙的な数値変換を行うコードでは、精度の重大な損失や、計算エラーや分岐エラーが発生する可能性があります。 すべての数値型を適切に調整する場合は、キャストだけでなく、意図的な分析とキャストの両方をお勧めします。
    • 予期されるパラメーター型の変更は避けてください。 API を呼び出すときに、明示的にキャストしない場合、暗黙的な変換によって問題が発生する可能性があります。たとえば、符号なし数値型ではなく符号付き数値型を使用する場合にバッファーをオーバーランします。
    • const-discarding 警告: 関数にパラメーターとして const 型が必要な場合は、オーバーライドするとバグや予期しない動作が発生する可能性があります。 この警告の理由は、const パラメーターがそのままであり、特定の API または関数を設計するときに制限を考慮するためです。
    • 互換性のないポインターまたはパラメーターの警告: この警告を無視すると、後で追跡するのが難しいバグが隠されることがあります。 これらの警告を排除すると、他のアプリケーションの問題の診断時間を短縮できます。
  • 一貫性のある CI/CD パイプラインを設定することは、古いアプリケーション リリースをデバッグするためのバイナリとその対応するシンボルを簡単に再構築できるため、持続可能な長期的なアプリケーション管理の鍵となります。 適切な分岐戦略は、リリースの追跡にも不可欠であり、バイナリ データの格納にコストのかかるディスク領域を回避します。
  • 可能な場合は、コードをより保守しやすい状態に保ちながら、コードベース全体でデータ ポインターとして使用できるように、ハード コーディングの代わりに、すべての一般的な固定文字列を (コマンド内printfなど) としてglobal const char*定義します。 実際のアプリケーションでは、ログや文字列操作 (、Succeeded、JSON プロパティ名などOK) から一般的なテキストを収集し、それを定数にグローバル化すると、読み取り専用データ メモリ セクション (.rodata とも呼ばれます) が節約され、他のセクションで使用できるフラッシュ メモリの節約 (より多くのコードの場合は .text など) に変換されます。 このシナリオは見過ごされることが多いですが、フラッシュ メモリを大幅に節約できます。

メモ

上記は、コンパイラの最適化 (gcc など -fmerge-constants ) をアクティブ化するだけでも実現できます。 この方法を選択した場合は、コンパイラの出力を調べて、必要な最適化が適用されていることを確認します。これは、コンパイラのバージョンによって異なる場合があるためです。

  • グローバル データ構造の場合は、可能な限り、動的に割り当てられたメモリにポインターを使用するのではなく、かなり小さい配列メンバーに固定長を与えることを検討してください。 例えば:
    typedef struct {
      int chID;
      ...
      char chName[SIZEOF_CHANNEL_NAME]; // This approach is preferable, and easier to use e.g. in a function stack.
      char *chName; // Unless this points to a constant, tracking a memory buffer introduces more complexity, to be weighed with the cost/benefit, especially when using multiple instances of the structure.
      ...
    } myConfig;
  • 特に頻繁に呼び出される関数内では、可能な限り動的メモリ割り当てを避けてください。
  • C で、メモリ バッファーへのポインターを返す関数を探し、参照されるバッファー ポインターとそれに関連するサイズを呼び出し元に返す関数に変換することを検討します。 これを行う理由は、バッファーへのポインターだけを返すと、多くの場合、呼び出し元のコードに問題が発生しています。これは、返されるバッファーのサイズが強制的に確認されないため、ヒープの整合性が危険にさらされる可能性があるためです。 例えば:
    // This approach is preferable:
    MY_RESULT_TYPE getBuffer(void **ptr, size_t &size, [...other parameters..])
    
    // This should be avoided, as it lacks tracking the size of the returned buffer and a dedicated result code:
    void *getBuffer([...other parameters..])

動的コンテナーとバッファー

リストやベクターなどのコンテナーは、埋め込み C アプリケーションでも頻繁に使用されます。標準ライブラリを使用する際のメモリ制限のため、通常は明示的にコード化するか、ライブラリとしてリンクする必要があります。 これらのライブラリの実装は、慎重に設計されていない場合、集中的なメモリ使用量をトリガーできます。

一般的な静的に割り当てられた配列や非常にメモリ動的な実装に加えて、増分割り当てアプローチをお勧めします。 たとえば、N 個の事前割り当て済みオブジェクトの空のキュー実装から始めます。(N+1) 番目のキュー プッシュでは、キューは固定 X 追加の事前割り当て済みオブジェクト (N=N+X) によって増加します。これは、キューに別の追加が現在の容量をオーバーフローし、X 個の追加の事前割り当て済みオブジェクトによってメモリ割り当てがインクリメントされるまで動的に割り当てられたままになります。 最終的には、未使用のメモリを再利用するために、(定期的に呼び出すにはコストがかかりすぎるため) 控えめに呼び出すための新しい圧縮関数を実装できます。

専用インデックスは、キューのアクティブ なオブジェクト数を動的に保持します。これは、追加のオーバーフロー保護のために最大値に上限を設定できます。

この方法では、従来のキュー実装での継続的なメモリ割り当てと割り当て解除によって生成される "chatter" が排除されます。 詳細については、「 メモリの管理と使用状況」を参照してください。 リスト、配列などの構造体に対して同様のアプローチを実装できます。