111

我正在为我们的项目审查其他人的 C++ 代码,该项目使用 MPI 进行高性能计算(10^5 - 10^6 核)。该代码旨在允许(可能)不同架构上的不同机器之间进行通信。他写了一条评论,内容大致如下:

我们通常使用newand delete,但在这里我使用mallocand free。这是必要的,因为某些编译器在new使用时会以不同的方式填充数据,从而导致在不同平台之间传输数据时出错。这不会发生在malloc.

这不符合我从标准newmalloc问题中了解到的任何内容。

new/delete 和 malloc/free 有什么区别?暗示编译器可以不同方式计算对象大小的想法(但为什么这与使用不同sizeof?)。

malloc &placement new vs. new是一个相当流行的问题,但只讨论在不new使用构造函数的地方使用构造函数malloc,这与此无关。

malloc 是如何理解对齐的?说内存可以保证与我之前的想法正确new对齐malloc

我的猜测是他在过去的某个时候误诊了自己的错误并推断出newmalloc给出了不同数量的填充,我认为这可能不是真的。但我无法通过谷歌或之前的任何问题找到答案。

帮助我,StackOverflow,你是我唯一的希望!

4

8 回答 8

26

IIRC 有一个挑剔的点。malloc保证返回与任何标准类型对齐的地址。::operator new(n)仅保证返回与不大于 n的任何标准类型对齐的地址,如果T不是字符类型new T[n],则只需要返回与 对齐的地址T

但这仅在您使用特定于实现的技巧时才相关,例如使用指针的底部几位来存储标志,或者依靠地址进行比严格需要的更多对齐。

它不会影响对象内的填充,无论您如何分配它占用的内存,它都必须具有完全相同的布局。所以很难看出这种差异如何导致数据传输错误。

是否有任何迹象表明该评论的作者对堆栈或全局对象中的对象有什么看法,无论他认为它们是“像 malloc 一样填充”还是“像 new 一样填充”?这可能会为这个想法的来源提供线索。

也许他很困惑,但也许他所说的代码不仅仅是malloc(sizeof(Foo) * n)vs之间的直接区别new Foo[n]。也许它更像是:

malloc((sizeof(int) + sizeof(char)) * n);

对比

struct Foo { int a; char b; }
new Foo[n];

也就是说,也许他是在“我使用 malloc”,但意思是“我手动将数据打包到未对齐的位置而不是使用结构”。实际上malloc不需要手动打包结构,但没有意识到这是一个较小程度的混乱。有必要定义通过线路发送的数据布局。使用结构时,不同的实现会以不同的方式填充数据。

于 2012-11-08T11:39:24.560 回答
5

您的同事可能已经想到了new[]/delete[]' 魔法 cookie(这是实现在删除数组时使用的信息)。new[]但是,如果使用从返回的地址开始的分配(与分配器的相反),这将不是问题。

包装似乎更有可能。ABI 的变化可能(例如)导致在结构末尾添加不同数量的尾随字节(这受对齐影响,也考虑数组)。使用 malloc,可以指定结构的位置,因此更容易移植到外部 ABI。通常通过指定传输结构的对齐和封装来防止这些变化。

于 2012-11-08T10:36:33.720 回答
3

对象的布局不能取决于它是使用malloc还是分配的new。它们都返回相同类型的指针,当您将此指针传递给其他函数时,它们将不知道对象是如何分配的。sizeof *ptr仅取决于 的声明ptr,而不是它的分配方式。

于 2012-11-08T10:44:08.733 回答
3

我想你是对的。填充由编译器完成,而不是newor malloc。即使您声明了一个数组或结构而没有使用newmalloc根本没有使用填充注意事项。无论如何,虽然我可以看到在平台之间移植代码时如何不同的实现newmalloc可能导致问题,但我完全看不到它们如何导致平台之间的数据传输问题。

于 2012-11-08T10:06:53.827 回答
0

这是我对这东西来自哪里的疯狂猜测。正如您所提到的,问题在于通过 MPI 传输数据。

就个人而言,对于我想通过 MPI 发送/接收的复杂数据结构,我总是实现序列化/反序列化方法,将整个内容打包/解包到字符数组中/从字符数组中解包。现在,由于填充,我们知道结构的大小可能大于其成员的大小,因此还需要计算数据结构的未填充大小,以便我们知道正在发送/接收的字节数。

例如,如果您想std::vector<Foo> A使用上述技术通过 MPI 发送/接收,则假设结果字符数组的大小通常是错误的A.size()*sizeof(Foo)。换句话说,每个实现序列化/反序列化方法的类也应该实现一个报告数组大小的方法(或者更好地将数组存储在容器中)。这可能成为错误背后的原因。但是,一种或另一种方式与此线程中指出的newvs无关。malloc

于 2012-11-12T18:37:09.137 回答
0

当我想使用 MS Visual 编译器控制我的普通旧数据结构的布局时,我使用#pragma pack(1). 我想大多数编译器都支持这样的预编译器指令,例如gcc

这具有将结构的所有字段一个接一个对齐的结果,没有空格。

如果另一端的平台也这样做(即使用填充 1 编译其数据交换结构),那么双方检索到的数据正好吻合。因此,我从来不用在 C++ 中使用 malloc。

在最坏的情况下,我会考虑重载 new 运算符,以便它执行一些棘手的事情,而不是直接在 C++ 中使用 malloc。

于 2012-11-14T00:05:18.190 回答
0

在 c++ 中: new关键字用于为某些数据结构分配一些特定的内存字节。例如,您已经定义了一些类或结构,并且想要为其对象分配内存。

myclass *my = new myclass();

或者

int *i = new int(2);

但在所有情况下,您都需要定义的数据类型(类、结构、联合、int、char 等),并且只分配其对象/变量所需的内存字节。(即;该数据类型的倍数)。

但是在 malloc() 方法的情况下,您可以分配任何字节的内存,并且您不需要始终指定数据类型。在这里,您可以在 malloc() 的几种可能性中观察到它:

void *v = malloc(23);

或者

void *x = malloc(sizeof(int) * 23);

或者

char *c = (char*)malloc(sizeof(char)*35);
于 2015-05-14T08:11:08.797 回答
-1

malloc 是一种函数类型,new 是 c++ 中的一种数据类型,如果我们使用 malloc,则必须使用类型转换,否则编译器会给您错误,如果我们使用新数据类型分配内存,则不需要进行类型转换

于 2015-12-23T12:28:20.430 回答