2012-12-08 10 views
6

私は90年代後半に書かれた既存のシングルスレッドCプログラムにCUDAを追加しようとしています。なぜgccとNVCC(g ++)は2つの異なる構造サイズを参照していますか?

これを行うには、CとC++(nvccはC++コンパイラ)という2つの言語を混在させる必要があります。

問題は、C++コンパイラが構造体を特定のサイズとして認識し、Cコンパイルが構造体のサイズがわずかに異なることです。それは良くないね。私は4バイトの不一致の原因を見つけることができないので、これは本当に困惑しています。

/usr/lib/gcc/i586-suse-linux/4.3/../../../../i586-suse-linux/bin/ld: Warning: size of symbol `tree' changed from 324 in /tmp/ccvx8fpJ.o to 328 in gpu.o 

私のC++は

#include <stdio.h> 
#include <stdlib.h> 
#include "assert.h" 
extern "C" 
{ 
#include "structInfo.h" //contains the structure declaration 
} 
... 

のように見え、私のCファイルには、私のメイクファイルは

のように見える

struct TB { 
    int nbranch, nnode, root, branches[NBRANCH][2]; 
     double lnL; 
} tree; 
... 

のように見えるstructInfo.hと

#include "structInfo.h" 
... 

のように見えます

PRGS = prog 
CC = cc 
CFLAGS=-std=gnu99 -m32 
CuCC = nvcc 
CuFlags =-arch=sm_20 
LIBS = -lm -L/usr/local/cuda-5.0/lib -lcuda -lcudart 
all : $(PRGS) 
prog: 
     $(CC) $(CFLAGS) prog.c gpu.o $(LIBS) -o prog 
gpu.o: 
     $(CuCC) $(CuFlags) -c gpu.cu 

なぜ私は別のホストコンパイルオプションを使用しなかったのですか?ホストコンパイルオプションは、2リリース前から廃止されていると思いますか?またit never appeared to do what it said it would do。 1つの4バイト整列および少なくとも8バイトアラインメントと相互に動作している:

nvcc warning : option 'host-compilation' has been deprecated and is ignored 
+0

パディングに違いはありますか? nvccとgcc(あなたがccとして使用していると思われるもの)は互換性がありますか?編集:実際には、nvccの埋め込み問題に関連する何かを読む、待機する –

+0

これは、32ビットまたは64ビットプラットフォームですか? [パラメータを移動する](https://devtalk.nvidia.com/default/topic/394418/padding-problem-nvcc-bug-/)を試してみてください。 –

+0

@EsaLakaniemiこれは32ビットプラットフォームです。 – Mikhail

答えて

12

GPUはすべてのデータに対して自然な整列を必要とします。 4バイトのintを4バイト境界に整列させる必要があり、8バイトのdoubleまたはlong longは8バイトの整列を持つ必要があります。 CUDAはこれをホストコードにも強制して、構造体がコードのホスト部分とデバイス部分の間でできるだけ互換性があることを確認します。一方、x86 CPUは、一般に、データが自然にアライメントされることを要求しない(ただし、アライメントが不十分なことによるパフォーマンスの低下の可能性がある)。この場合

、CUDAは、8バイト境界に構造体の二重構成要素を整列させる必要があります。奇数のintコンポーネントがdoubleに先行するので、これにはパディングが必要です。このような構造体の配列では、各構造体が8バイト境界でなければならないため、構造体のサイズは8バイトの倍数でなければならないため、コンポーネントの順序を切り替える、つまり二重コンポーネントを最初に置くことは役に立ちませんまた、パディングが必要です。

フラグ-malign-doubleを渡し、CUDAが行うのと同じ方法でダブルスを整列させるために、GCCを強制します。

5

2つのコンパイラによって適用される異なるパディングのように思えます。コンパイラ固有の#pragmaディレクティブ(コンパイラ固有のドキュメント#pragmaを参照)を使用して、必要な配置を強制することができます。

+0

'#pragma pack(4)'と '#pragma pack(8)'は役に立たないようですが、同じエラーが発生します。どのようにgccのためにこれを行うのですか? – Mikhail

+5

私はgccのために必要なのは、コンパイラのフラグ-malign-doubleですと思います。 GPUはすべてのデータに対して自然な整列を必要とするため、CUDAはこれをホスト上で強制して、構造体がコードのホスト部分とデバイス部分の間で互換性があることを確認します。structのdoubleの前にあるintの数は奇数なので、構造体を埋め込む必要があります。代わりに、構造体のコンポーネントを並べ替えることで、doubleが最初のコンポーネントになるようにすることができます。 – njuffa

+0

@njuffa並べ替えは機能しませんが、 '-malign-double'が機能しました。私はあなたに信用を与えることができるように答えとして投稿するべきです。ありがとう! – Mikhail

2

2つの異なるCコンパイラが両方とも同じタイプの同じ表現を使用する保証はありません。どちらも、十分に詳細に表現を指定する外部標準(ABI)に準拠していなければなりません。

1つのコンパイラではdoubleを4バイト境界に合わせる必要があり、もう1つは8バイト境界にする必要があります。両方の選択肢は、CおよびC++標準に関する限り完全に有効です。

あなたはあなたの構造体のすべてのメンバーのサイズとオフセットをプリントアウトすることにより、これをより詳細に調査することができます。

printf("nbranch: size %3u offset %3u\n", 
     (unsigned)sizeof tree.nbranch, 
     (unsigned)offsetof(struct TB, nbranch)); 
/* and similarly for the other members */ 

あり異なる配置を指定するには、コンパイラ固有の方法かもしれませんが、そのような技術はnot always safeです。

理想的な解決策は、CおよびC++コードに同じコンパイラを使用することです。 CはC++のサブセットではありませんが、一般に既存のCコードを変更してC++としてコンパイルするのは難しくありません。

また、両方のコンパイラが同じように配置されるように構造体定義を並べ替えることができます。最初にdoubleのメンバーを配置することは有効です。これはまだ動作することが保証されておらず、どちらのコンパイラの将来のバージョンでも破損する可能性がありますが、(おそらく)です。

構造の最後にパディングがある可能性があることを忘れないでください。これは、構造体の配列の適切な位置合わせを保証するために時には必要です。 sizeof (struct TB)を参照し、最後に宣言されたメンバーのサイズとオフセットと比較してください。

別の可能性:明示的に未使用のメンバーを挿入して、一貫性のある配置を強制してください。たとえば、あなたが持っている場合とします

struct foo { 
    uint16_t x; 
    uint32_t y; 
}; 

と1つのコンパイラが16ビットでyを置き、もう一方は、パディングの16ビットと32ビットでそれを置きます。あなたが定義を変更した場合:

struct foo { 
    uint16_t x; 
    uint16_t unused_padding; 
    uint32_t y; 
}; 

あなたはxyは、両方のコンパイラの下で同じオフセットしている持っている可能性が高くなります。あなたはまだすべてが一貫していることを確認するために実験する必要があります。

CおよびC++コードは、同じプログラム(右?)の一部であることを行っているので、あなたはバイト順を変えるようなことを心配する必要はありません。構造体型の値を別々のプログラム間で転送したい場合は、ファイルに格納したり、ネットワーク経由で転送したりするなど、構造体の値をバイト列にシリアル化する一貫性のある方法を定義する必要があります。

関連する問題