首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为什么gcc和NVCC (g++)看到两种不同的结构尺寸?

为什么gcc和NVCC (g++)看到两种不同的结构尺寸?
EN

Stack Overflow用户
提问于 2012-12-09 06:13:56
回答 3查看 2.5K关注 0票数 6

我正在尝试将CUDA添加到一个现有的单线程C程序中,该程序是在90年代末编写的。

为此,我需要混合两种语言,C和C++ (nvcc是一个c++编译器)。

问题是C++编译器将结构视为特定的大小,而C编译器将相同的结构视为略有不同的大小。那可不好。我真的对此感到困惑,因为我找不到4字节差异的原因。

代码语言:javascript
复制
/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++看起来像

代码语言:javascript
复制
#include <stdio.h>
#include <stdlib.h>
#include "assert.h"
extern "C"
{
#include "structInfo.h" //contains the structure declaration
}
...

我的C文件看起来像这样

代码语言:javascript
复制
#include "structInfo.h"
...

其中structInfo.h看起来像

代码语言:javascript
复制
struct TB {
   int  nbranch, nnode, root, branches[NBRANCH][2];
         double lnL;
}  tree;
...

我的make文件看起来像这样

代码语言:javascript
复制
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

代码语言:javascript
复制
nvcc warning : option 'host-compilation' has been deprecated and is ignored
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2012-12-09 09:50:12

GPU需要所有数据的自然对齐,例如,4字节的int需要与4字节的边界对齐,8字节的double或long需要8字节的对齐。CUDA也对主机代码强制执行这一点,以确保结构在代码的主机和设备部分之间尽可能地兼容。另一方面,x86 CPU通常不要求数据自然对齐(尽管缺乏对齐可能会导致性能损失)。

在这种情况下,CUDA需要将结构的双分量与8字节边界对齐。因为在double之前有奇数个int组件,所以这需要填充。改变组件的顺序,也就是把双倍组件放在第一位,这是没有用的,因为在这样的结构的数组中,每个结构必须是8字节对齐的,因此结构的大小必须是8字节的倍数才能实现,这也需要填充。

要强制gcc以与CUDA相同的方式对齐替身,请传递标志-malign-double

票数 17
EN

Stack Overflow用户

发布于 2012-12-09 06:24:23

看起来两个编译器应用了不同的填充:一个使用4字节对齐,另一个使用至少8字节对齐。您应该能够通过特定于编译器的#pragma指令强制实现所需的对齐(请查看有关特定#pragma的编译器文档)。

票数 5
EN

Stack Overflow用户

发布于 2012-12-09 07:16:54

不能保证两个不同的C编译器对同一类型使用相同的表示形式--除非它们都符合某种外部标准( ABI),该标准指定了足够详细的表示形式。

这很可能是填充方面的差异,其中一个编译器要求double是4字节对齐的,而另一个编译器要求它是8字节对齐的。就C和C++标准而言,这两种选择都是完全有效的。

您可以通过打印出结构中所有成员的大小和偏移量来更详细地研究这一点:

代码语言:javascript
复制
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),并将其与最后声明的成员的大小和偏移量进行比较。

另一种可能是:插入显式未使用的成员以强制一致对齐。例如,假设您有:

代码语言:javascript
复制
struct foo {
    uint16_t x;
    uint32_t y;
};

一个编译器将y设置为16位,另一个编译器将其设置为具有16位填充的32位。如果将定义更改为:

代码语言:javascript
复制
struct foo {
    uint16_t x;
    uint16_t unused_padding;
    uint32_t y;
};

然后,您更有可能让xy在两个编译器下具有相同的偏移量。你仍然需要进行实验,以确保一切都是一致的。

由于C和C++代码将是同一个程序的一部分(对吗?),所以您不必担心改变字节顺序之类的事情。如果您想要在不同的程序之间传输结构类型的值,例如通过将它们存储在文件中或通过网络传输它们,您可能需要定义一种一致的方法来将结构值序列化为字节序列,反之亦然。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/13782480

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档