3

免責事項: この質問は一般的な概念に関するものです。実際のアプリケーションのコンテキスト全体を提供する必要がなくても、ここで明確に質問できるように、質問を「軽視」しました。「どうしてそんなことをするの?」などのコメントがたくさん寄せられることはすでに予想できます。しかし、質問を額面どおりに受け取っていただければ幸いです。

事前定義された構造体から実行時に C のデータ構造を動的に合成したいとします。

この質問をする方法を知る最良の方法は、コード サンプルを使用することです。

Foo以下では、との 2 つの構造体を定義していBarます。また、構造体を定義してFooBar、コンパイル時に生成される複合型と実行時に生成される "動的に合成された" 型との少なくとも 1 つの違いを説明します。

#include <stdlib.h>
#include <stdio.h>

typedef struct Foo {
    char junk1;
    char junk2;
} Foo;

typedef struct Bar {
    int junk3;
    int junk4;
} Bar;

typedef struct FooBar {
    Foo foo;
    Bar bar;
} FooBar;

int main()
{
    printf("Sizes: %li, %li, %li\n", sizeof(Foo), sizeof(Bar), sizeof(FooBar));
    // Prints: Sizes: 2, 8, 12
    // Because Foo is aligned on 1-byte boundaries and has total size of 2 bytes.
    // Bar is aligned on 4-byte boundaries and has total size of 8 bytes.
    // But FooBar is aligned on 4-byte boundaries due to the ints in Foo. Therefore,
    // the compiler added 2-bytes of padding after the foo member.

    // The following "works", but only allocates 10 bytes, and
    // "Bar" members are now "misaligned":
    void * syntheticFooBar = malloc(sizeof(Foo) + sizeof(Bar));
    ((Foo*)syntheticFooBar)->junk1 = 1;
    ((Foo*)syntheticFooBar)->junk2 = 2;
    ((Bar*)(syntheticFooBar + sizeof(Foo)))->junk3 = 3;
    ((Bar*)(syntheticFooBar + sizeof(Foo)))->junk4 = 4;

    free(syntheticFooBar);
    return 0;
}

だから私の質問は次のようになります:

1.) 適切なデータ配置がないと、パフォーマンスにどの程度影響しますか? 合成構造の「メンバー」へのアクセスに伴うオーバーヘッドを考えると、データアライメントは重要な要因ですか?

2.) ランタイム合成の制約を考慮して、これを行うためのより良い方法はありますか?

4

1 に答える 1

1

1.) 適切なデータ配置がないと、パフォーマンスにどの程度影響しますか? 合成構造の「メンバー」へのアクセスに伴うオーバーヘッドを考えると、データアライメントは重要な要因ですか?

これは、CPU アーキテクチャとコンパイラに完全に依存しています。一部のシステムではパフォーマンスが低下するだけかもしれませんが、他のシステムではクラッシュする可能性があります。

2.) ランタイム合成の制約を考慮して、これを行うためのより良い方法はありますか?

実行時に適切に配置された合成構造体を作成する方法の例を次に示します。

#include <stdio.h>
#include <stdlib.h>

typedef struct {
  char junk1;
  char junk2;
} A;

typedef struct {
  int junk3;
  int junk4;
} B;

typedef struct {
  double junk5;
  char junk6;
} C;

static size_t roundUp(size_t value,size_t factor)
{
  return value+factor-1-((value+factor-1)%factor);
}

#define alignof(type) (sizeof(struct {type a;char b;})-sizeof(type))

int main(int argc,char **argv)
{
  size_t offsets[3];
  size_t pos = 0;

  pos = roundUp(pos,alignof(A));
  offsets[0] = pos;
  pos += sizeof(A);

  pos = roundUp(pos,alignof(B));
  offsets[1] = pos;
  pos += sizeof(B);

  pos = roundUp(pos,alignof(C));
  offsets[2] = pos;
  pos += sizeof(C);

  {
    char *foobar = malloc(pos);
    A *a = (A *)(foobar + offsets[0]);
    B *b = (B *)(foobar + offsets[1]);
    C *c = (C *)(foobar + offsets[2]);
    a->junk1 = 1;
    a->junk2 = 2;
    b->junk3 = 3;
    b->junk4 = 4;
    c->junk5 = 5;
    c->junk6 = 6;
    free(foobar);
  }
  return 0;
}

特定の構造体のアラインメントは、元の構造体と追加の char を使用して構造体を作成することによって決定されます。コンパイラは、これらの構造体の配列を使用する場合に必要なアラインメントが維持されるように、十分なパディングを自動的に追加します。そのため、サイズの違いを測定することで、適切なアラインメントが得られます。

アラインメント情報を使用して、合成された構造体の各メンバーがどこに存在する必要があるかを示すテーブルを作成できます。配置の最も近い倍数に位置を切り上げることによって、各メンバーの位置が適切に配置されていることを確認する必要があります。

これを任意の数のメンバーに一般化できるはずです。

これらの合成された構造体の配列を作成できるように、合成された構造体の全体的なサイズ ( sizeof() が返す可能性があるもの) を決定したい場合は、組み合わせたアライメント要件を取得し、最終的な位置を丸める必要があることに注意してください。その要因。組み合わせたアライメントは、個々のアライメントの最小公倍数になります。

于 2013-08-16T04:44:48.780 に答える