我正在审查其他人使用MPI进行高性能计算(10 ^ 5 - 10 ^ 6核心)的项目的C ++代码。该代码旨在允许不同体系结构上(可能)不同机器之间的通信。他写了一条评论,说的是:
我们通常使用
new
和delete
,但我在这里使用malloc
和free
。这是必要的,因为在使用new
时,某些编译器将以不同方式填充数据,从而导致在不同平台之间传输数据时出错。malloc
不会发生这种情况。
这与我在标准new
与malloc
问题中所知道的任何内容都不相符。
What is the difference between new/delete and malloc/free?暗示编译器可以不同地计算对象的大小(但那为什么与使用sizeof
不同?)。
malloc & placement new vs. new是一个相当受欢迎的问题,但只讨论new
使用malloc
没有的构造函数,这与此无关。
how does malloc understand alignment?表示内存可以保证与我之前想到的new
或malloc
正确对齐。
我的猜测是,他在过去的某个时间误诊了自己的错误,并推断出new
和malloc
会给出不同数量的填充,我认为可能并非如此。但我无法通过谷歌或以前的任何问题找到答案。
帮助我,StackOverflow,你是我唯一的希望!
答案 0 :(得分:25)
malloc
保证返回与任何标准类型对齐的地址。 ::operator new(n)
仅保证返回与任何标准类型不大于n 对齐的地址,如果T
不是字符类型,则new T[n]
仅为需要返回与T
对齐的地址。
但这只是在你玩特定于实现的技巧时才有用,例如使用指针的最后几位来存储标志,或者依赖地址来获得比它严格需要的更多的对齐。
它不会影响对象内的填充,无论您如何分配它占用的内存,它都必须具有完全相同的布局。因此,很难看出这种差异如何导致传输数据的错误。
是否有任何迹象表明该评论的作者对堆栈或全局变量中的对象有何看法,无论他认为他们是“填充像malloc”还是“填充像新的”?这可能会为这个想法的来源提供线索。
也许他很困惑,但也许他所谈论的代码不仅仅是malloc(sizeof(Foo) * n)
与new Foo[n]
之间的直接差异。也许它更像是:
malloc((sizeof(int) + sizeof(char)) * n);
VS
struct Foo { int a; char b; }
new Foo[n];
也就是说,也许他是说“我使用malloc”,但意味着“我手动将数据打包到未对齐的位置而不是使用结构”。实际上,为了手动打包结构,不需要malloc
,但没有意识到这是一个较小程度的混淆。有必要定义通过线路发送的数据布局。当使用 struct 时,不同的实现将以不同方式填充数据。
答案 1 :(得分:5)
您的同事可能已经考虑了new[]/delete[]
的神奇Cookie(这是删除数组时实现所使用的信息)。但是,如果使用从new[]
返回的地址开始的分配(而不是分配器),这不会成为问题。
包装似乎更有可能。 ABI的变化可以(例如)导致在结构的末尾添加不同数量的尾随字节(这受到对齐的影响,也考虑数组)。使用malloc,可以指定结构的位置,从而更容易移植到外国ABI。通常通过指定转移结构的对齐和包装来防止这些变化。
答案 2 :(得分:3)
我认为你是对的。填充由编译器完成,而不是new
或malloc
。即使您在未使用new
或malloc
的情况下声明了数组或结构,也会应用填充注意事项。在任何情况下,虽然我可以看到new
和malloc
的不同实现在平台之间移植代码时如何导致问题,但我完全看不出它们如何导致在平台之间传输数据的问题。
答案 3 :(得分:3)
对象的布局不能取决于它是使用malloc
还是new
分配的。它们都返回相同类型的指针,当您将此指针传递给其他函数时,它们将不知道对象是如何分配的。 sizeof *ptr
仅取决于ptr
的声明,而不是它的分配方式。
答案 4 :(得分:0)
这是我猜测这件事的来源。正如您所提到的,问题在于通过MPI进行数据传输。
就个人而言,对于我想通过MPI发送/接收的复杂数据结构,我总是实现序列化/反序列化方法,将整个事物打包/解包到字符数组中。现在,由于填充,我们知道结构的大小可能大于其成员的大小,因此还需要计算数据结构的未填充大小,以便我们知道发送/接收的字节数。 / p>
例如,如果您希望使用上述技术通过MPI发送/接收std::vector<Foo> A
,则假设生成的字符数组的大小通常为A.size()*sizeof(Foo)
是错误的。换句话说,实现序列化/反序列化方法的每个类也应该实现一个报告数组大小的方法(或者更好地将数组存储在容器中)。这个可能成为bug背后的原因。但是,这个或那个与new
vs malloc
无关,如本主题所述。
答案 5 :(得分:0)
当我想控制普通旧数据结构的布局时,使用MS Visual编译器我使用#pragma pack(1)
。我想大多数编译器都支持这样的预编译器指令,例如gcc。
这样做的结果是将结构的所有字段一个接一个地对齐,没有空格。
如果另一端的平台做同样的事情(即编译其数据交换结构的填充为1),则两侧检索到的数据都很合适。 因此,我从未必须在C ++中使用malloc。
最糟糕的是,我会考虑重载new运算符,因为它执行一些棘手的事情,而不是直接在C ++中使用malloc。
答案 6 :(得分:0)
在c ++中: new
关键字用于为某些数据结构分配一些特定的内存字节。例如,您已经定义了一些类或结构,并且您希望为其对象分配内存。
myclass *my = new myclass();
或
int *i = new int(2);
但在所有情况下,您都需要定义的数据类型(class,struct,union,int,char等...),并且只分配其对象/变量所需的内存字节。 (即;该数据类型的倍数)。
但是在malloc()方法的情况下,您可以分配任何内存字节,并且您无需始终指定数据类型。在这里你可以用malloc()的几种可能性来观察它:
void *v = malloc(23);
或
void *x = malloc(sizeof(int) * 23);
或
char *c = (char*)malloc(sizeof(char)*35);
答案 7 :(得分:-1)
malloc是一种功能 而new是c ++中的一种数据类型 在c ++中,如果我们使用malloc而不是必须使用类型转换,否则编译器会给你错误 如果我们使用新数据类型来分配内存而不需要进行类型转换