12

パディング効果を減らすために、構造体のフィールドを手動で並べ替えるのに数分を費やしました[1]。私の直感では、Perl スクリプトを作成したり、この種の最適化を行ったりすることに時間を費やしたほうがよいのではないかと考えています。

私の質問は、これも冗長かどうかです。私が認識していないツール、または構造体をパックするために[2]をオンにできるはずのコンパイラ機能がすでにありますか?

この問題は、これがいくつかの異なるアーキテクチャ間で一貫して最適化される必要があるという事実によってさらに複雑になります。そのため、使用するツールが何であれ、異なる構造体アラインメントとポインター サイズも考慮できる必要があります。

EDIT:簡単な説明 - 私がしたいのは、パディングなしでコンパイルするように構造体を「パック」するのではなく、パディングを避けるためにソースコードのフィールドを並べ替えることです。

編集 #2: 別の問題: 構成によっては、一部のデータ型のサイズも変わる場合があります。明らかなものは、さまざまなアーキテクチャのポインターとポインター差分ですが、浮動小数点型 (「正確さ」に応じて 16、32 または 64 ビット)、チェックサム (「速度」に応じて 8 または 16 ビット) などもあります。その他の明らかでないもの。

[1] 問題の構造体は、組み込みデバイスで何千回もインスタンス化されるため、構造体の 4 バイト削減ごとに、このプロジェクトの成功と失敗の違いを意味する可能性があります。

[2] 利用可能なコンパイラは、GCC 3.* および 4.* 、Visual Studio、TCC、ARM ADS 1.2、RVCT 3.*、およびその他いくつかの不明なものです。

4

8 に答える 8

7

ストレージから絞り出すことができるすべての単語が重要である場合は、手動で構造体を最適化することをお勧めします。ツールはメンバーを最適に配置できますが、たとえば、16 ビットで保存しているこの値が実際には 1024 を超えないことを認識していないため、この値の上位 6 ビットを盗むことができますここで...

したがって、人間はほぼ確実に、この仕事でロボットを打ち負かします。

[編集] しかし、各アーキテクチャの構造体を手動で最適化したくないようです。サポートするアーキテクチャが本当にたくさんあるのではないでしょうか?

この問題は一般的な解決策には適していないと思いますが、ドメインの知識を、各アーキテクチャの構造体定義を生成するカスタム Perl/Python/something スクリプトにエンコードできる場合があります。

また、すべてのメンバーのサイズが 2 のべき乗である場合、メンバーをサイズでソートするだけで最適なパッキングが得られます (最大のものから順に)。その場合、古き良きマクロベースの構造体構築を使用することができます -このようなもの:

#define MYSTRUCT_POINTERS      \
    Something*  m_pSomeThing;  \
    OtherThing* m_pOtherThing; 

#define MYSTRUCT_FLOATS        \
    FLOAT m_aFloat;            \
    FLOAT m_bFloat;

#if 64_BIT_POINTERS && 64_BIT_FLOATS
    #define MYSTRUCT_64_BIT_MEMBERS MYSTRUCT_POINTERS MYSTRUCT_FLOATS
#else if 64_BIT_POINTERS
    #define MYSTRUCT_64_BIT_MEMBERS MYSTRUCT_POINTERS
#else if 64_BIT_FLOATS
    #define MYSTRUCT_64_BIT_MEMBERS MYSTRUCT_FLOATS
#else
    #define MYSTRUCT_64_BIT_MEMBERS
#endif

// blah blah blah

struct MyStruct
{
    MYSTRUCT_64_BIT_MEMBERS
    MYSTRUCT_32_BIT_MEMBERS
    MYSTRUCT_16_BIT_MEMBERS
    MYSTRUCT_8_BIT_MEMBERS
};
于 2009-05-15T08:16:10.463 に答える
6

通常Perlインストールに含まれているpstructと呼ばれるPerlスクリプトがあります。スクリプトは、構造体メンバーのオフセットとサイズをダンプします。pstructを変更するか、その出力を開始点として使用して、構造を希望どおりにパックするユーティリティを作成できます。

$ cat foo.h 
struct foo {
    int x;
    char y; 
    int b[5];
    char c;
};

$ pstruct foo.h
struct foo {
  int                foo.x                      0       4
  char               foo.y                      4       1
                     foo.b                      8      20
  char               foo.c                     28       1
}
于 2009-05-15T08:26:22.153 に答える
3

ほとんどのCコンパイラは、奇妙なことを実行できるという事実に基づいてこれを実行しません(構造体の要素のアドレスを取得し、ポインタマジックを使用して残りの要素にアクセスし、コンパイラをバイパスするなど)。有名な例は、リストの先頭と末尾として保護ノードを使用したAmigaOSの二重リンクリストです(これにより、リストをトラバースするときにifを回避できます)。ガーディアンヘッドノードには常にがpred == nullあり、テールノードにはがありますnext == null。開発者は2つのノードを単一の3ポインター構造体にロールしましたhead_next null tail_pred。のアドレスhead_nextまたはnullをヘッドノードとテールノードのアドレスとして使用することにより、4バイトと1つのメモリ割り当てを節約しました(構造全体が1回だけ必要だったため)。

したがって、最善の策は、構造を擬似コードとして記述し、そこから実際の構造を作成するプリプロセッサスクリプトを作成することです。

于 2009-05-15T08:19:42.763 に答える
0

プラットフォーム/コンパイラにも依存します。前述のように、ほとんどのコンパイラはすべてを4バイトアラインメントにパディングします(またはさらに悪いことに!)。したがって、2つのショートとロングの構造体を想定します。

short
long
short

12バイトを使用します(2 * 2バイトのパディングを使用)。

に並べ替える

short
short
long

コンパイラがデータアクセスを高速化するためにパディングするため、12バイトを使用します(これは、メモリ使用量よりも高速アクセスを優先するため、ほとんどのデスクトップのデフォルトです)。組み込みシステムにはさまざまなニーズがあるため、#pragmaパックを使用する必要があります。

並べ替えるツールについては、構造体のレイアウトを(手動で)再編成して、さまざまなタイプが一緒に配置されるようにします。最初にすべてのショートパンツを入れてから、すべてのロングパンツを入れます。パッキングを完了する場合は、とにかくツールがそれを実行します。タイプ間の遷移点の中央に2バイトのパディングがあるかもしれませんが、それについて心配する価値はないと思います。

于 2009-05-15T08:30:35.140 に答える
0

#pragma pack をご覧ください。これにより、コンパイラが構造内の要素を整列する方法が変更されます。これを使用して、それらをスペースなしで密集させることができます。

詳細はこちら

于 2009-05-15T08:06:49.190 に答える
0

コンパイラは、構造体のフィールドを独自のヘッドで並べ替えることはできません。標準では、フィールドは定義された順序で配置する必要があります。他のことをすると、微妙な方法でコードが壊れる可能性があります。

あなたが書いているように、効率的な方法でフィールドをシャッフルするある種のコードジェネレーターを作成することはもちろん完全に可能です。しかし、私はこれを手動で行うことを好みます。

于 2009-05-15T10:15:28.367 に答える