2

最近、アラインメントの制約に違反しているのではないかと心配しているコードベースを見ました。以下に示す最小限の例を作成するために、それをスクラブしました。簡単に言えば、プレーヤーは次のとおりです。

  • プール。これは、「効率的」の定義のために、メモリを効率的に割り当てるクラスです。プールは、要求されたサイズに合わせて調整されたメモリのチャンクを返すことが保証されています。

  • オブジェクトリスト。このクラスは、同種のオブジェクトのコレクションを格納します。オブジェクトの数が特定のしきい値を超えると、内部表現がリストからツリーに変更されます。Obj_listのサイズは 1 つのポインターです (64 ビット プラットフォームでは 8 バイト)。もちろん、人口の多い店舗はそれを上回ります。

  • 集計します。このクラスは、システムで非常に一般的なオブジェクトを表します。その歴史は初期の 32 ビット ワークステーション時代にまでさかのぼり、その結果、できるだけ少ないスペースを使用するように (同じ 32 ビット時代に) 「最適化」されました。Aggregateは空にすることも、任意の数のオブジェクトを管理することもできます。

この例では、Aggregateアイテムは常にPoolから割り当てられるため、常に整列されます。この例でObj_listが出現するのはAggregateオブジェクトの「非表示」メンバーのみであるため、常に配置 newを使用して割り当てられます。サポート クラスは次のとおりです。

class Pool
{
public:
   Pool();
   virtual ~Pool();
   void *allocate(size_t size);
   static Pool *default_pool();   // returns a global pool
};

class Obj_list
{
public:
   inline void *operator new(size_t s, void * p) { return p; }

   Obj_list(const Args *args);
   // when constructed, Obj_list will allocate representation_p, which
   // can take up much more space.

   ~Obj_list();

private:
   Obj_list_store *representation_p;
};

そして、ここに集計があります。メンバー宣言member_list_store_dに注意してください。

// Aggregate is derived from Lesser, which is twelve bytes in size
class Aggregate : public Lesser
{
public:
   inline void *operator new(size_t s) {
      return Pool::default_pool->allocate(s);
   }

   inline void *operator new(size_t s, Pool *h) {
      return h->allocate(s);
   }

public:

   Aggregate(const Args *args = NULL);
   virtual ~Aggregate() {};

   inline const Obj_list *member_list_store_p() const;

protected:
   char member_list_store_d[sizeof(Obj_list)];
};

私が最も関心を持っているのは、そのデータ メンバーです。初期化とアクセスの擬似コードは次のとおりです。

Aggregate::Aggregate(const Args *args)
{
   if (args) {
      new (static_cast<void *>(member_list_store_d)) Obj_list(args);
   }
   else {
      zero_out(member_list_store_d);
   }
}

inline const Obj_list *Aggregate::member_list_store_p() const
{
   return initialized(member_list_store_d) ? (Obj_list *) &member_list_store_d : 0;
}

char 配列を、 NULL またはクラスのインスタンスに初期化されたObj_list型へのポインターに置き換えることを提案したくなるかもしれません。これにより適切なセマンティクスが得られますが、メモリ コストがシフトするだけです。メモリが依然として貴重な場合 (これは EDA データベース表現である可能性があります)、Aggregateオブジェクトにメンバーがある場合、char 配列をObj_listへのポインターに置き換えると、ポインターが 1 つ増えることになります。

それに加えて、ここでの主な問題であるアラインメントから気をそらされたくありません。上記の構成には問題があると思いますが、「システム/ライブラリ」 newのアライメント動作に関する漠然とした議論以上のものを標準で見つけることはできません。

では、上記の構造は時折パイプの失速を引き起こす以上のことをするのでしょうか?

編集:埋め込まれた char 配列を使用してアプローチを置き換える方法があることを認識しています。元の建築家もそうでした。メモリが貴重だったので、彼らはそれらを破棄しました。さて、そのコードに触れる理由があれば、おそらく変更するでしょう。

しかし、このアプローチに内在するアラインメントの問題についての私の質問は、人々が解決してくれることを願っています。ありがとう!

4

5 に答える 5

2

わかりました-それを正しく読む機会がありました。配置に問題があり、char配列にObj_listとしてアクセスすると未定義動作が発生します。ほとんどの場合、プラットフォームは次の3つのいずれかを実行します。それを回避するか、実行時のペナルティで回避するか、バスエラーでクラッシュすることがあります。

これを修正するためのポータブルオプションは次のとおりです。

  • mallocまたはグローバル割り当て関数を使用してストレージを割り当てますが、これはコストがかかりすぎると思います。
  • Arkadiyが言うように、バッファをObj_listメンバーにします。

    Obj_list list;
    

しかし、あなたは今、建設費を払いたくないのです。これを軽減するには、このインスタンスを作成するためだけに使用されるインラインの何もしないコンストラクターを提供します。これは、デフォルトのコンストラクターが行うように投稿されたものです。このルートをたどる場合は、dtorを呼び出すことを強く検討してください

list.~Obj_list();

このストレージに新しい配置を行う前に。

そうでなければ、移植性のないオプションが残っていると思います。プラットフォームの不整合なアクセスの許容範囲に依存するか、コンパイラが提供する移植性のないオプションを使用します。

免責事項:私が組合などのトリックを見逃している可能性は十分にあります。それは珍しい問題です。

于 2008-10-30T13:24:56.687 に答える
1

構造の整列を確実にしたい場合は、

// MSVC
#pragma pack(push,1)

// structure definitions

#pragma pack(pop)

// *nix
struct YourStruct
{
    ....
} __attribute__((packed));

Aggregate で char 配列の 1 バイト アラインメントを確保するには

于 2008-10-29T23:55:29.660 に答える
1

アラインメントは、デフォルトに従ってコンパイラによって選択されます。これは、おそらく GCC / MSVC では 4 バイトになります。

これは、特定のアライメントが必要なコード (SIMD/DMA) がある場合にのみ問題になります。この場合、コンパイラ ディレクティブを使用して member_list_store_d が整列されていることを確認するか、サイズを (alignment-1) だけ増やして適切なオフセットを使用する必要があります。

于 2008-10-29T17:29:48.490 に答える
1

Aggregate 内に Obj_list のインスタンスを単純に持つことはできますか? IOW、の線に沿った何か

class Aggregate : public Lesser { ... protected: Obj_list リスト; };

何かが欠けているに違いありませんが、なぜこれが悪いのかわかりません。

あなたの質問に関しては、完全にコンパイラに依存しています。ただし、ほとんどのコンパイラは、正しいアクセスのためにメンバーの型をそのように配置する必要がない場合でも、デフォルトですべてのメンバーをワード境界に配置します。

于 2008-10-30T00:07:05.317 に答える
0

char配列member_list_store_dをmallocまたはグローバル演算子new[]で割り当てます。どちらの場合も、任意のタイプに合わせてストレージが調整されます。

編集:OPをもう一度読んでください-別のポインタにお金を払いたくないのです。午前中にまた読みます。

于 2008-10-29T23:44:10.557 に答える