112

I'm reviewing someone else's C++ code for our project that uses MPI for high-performance computing (10^5 - 10^6 cores). The code is intended to allow for communications between (potentially) different machines on different architectures. He's written a comment that says something along the lines of:

We'd normally use new and delete, but here I'm using malloc and free. This is necessary because some compilers will pad the data differently when new is used, leading to errors in transferring data between different platforms. This doesn't happen with malloc.

This does not fit with anything I know from standard new vs malloc questions.

What is the difference between new/delete and malloc/free? hints at the idea that the compiler could calculate the size of an object differently (but then why does that differ from using sizeof?).

malloc & placement new vs. new is a fairly popular question but only talks about new using constructors where malloc doesn't, which isn't relevant to this.

how does malloc understand alignment? says that memory is guaranteed to be properly aligned with either new or malloc which is what I'd previously thought.

My guess is that he's misdiagnosed his own bug some time in the past and deduced that new and malloc give different amounts of padding, which I think probably isn't true. But I can't find the answer with Google or in any previous question.

Help me, StackOverflow, you're my only hope!

Community
  • 1
  • 1
hcarver
  • 7,126
  • 4
  • 41
  • 67
  • 33
    +1 for the research of various SO threads alone! – iammilind Nov 08 '12 at 10:04
  • 7
    +1 Easily one of the best "help-myself-before-I-ask-others" research jobs I've' seen on SO in a LONG time. Wish I could upvote this a few more times. – WhozCraig Nov 08 '12 at 10:21
  • 1
    Does the transfer code assume that the data is aligned in any specific way, e.g. that it starts at an eight-byte boundary? This could differ between `malloc` and `new`, as `new` in some environments allocate a block, adds some data to the beginning and return a pointer to a location right after this data. (I agree with the others, inside the data block, `malloc` and `new` must use the same kind of padding.) – Lindydancer Nov 08 '12 at 11:04
  • 1
    **Wow** I wasn't expecting this question to be this popular! @Lindydancer, I don't think any 8-byte boundary is assumed. Interesting point though. – hcarver Nov 08 '12 at 12:25
  • 1
    One reason to use one allocation method over another is when "somebody else" is doing the release of the object. If this "somebody else" deletes the object using free, you must allocate using malloc. (An the pad issue is a red herring.) – Lindydancer Nov 08 '12 at 15:31
  • That's true but not the issue here. – hcarver Nov 08 '12 at 15:32

8 Answers8

27

IIRC there's one picky point. malloc is guaranteed to return an address aligned for any standard type. ::operator new(n) is only guaranteed to return an address aligned for any standard type no larger than n, and if T isn't a character type then new T[n] is only required to return an address aligned for T.

But this is only relevant when you're playing implementation-specific tricks like using the bottom few bits of a pointer to store flags, or otherwise relying on the address to have more alignment than it strictly needs.

It doesn't affect padding within the object, which necessarily has exactly the same layout regardless of how you allocated the memory it occupies. So it's hard to see how the difference could result in errors transferring data.

Is there any sign what the author of that comment thinks about objects on the stack or in globals, whether in his opinion they're "padded like malloc" or "padded like new"? That might give clues to where the idea came from.

Maybe he's confused, but maybe the code he's talking about is more than a straight difference between malloc(sizeof(Foo) * n) vs new Foo[n]. Maybe it's more like:

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

vs.

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

That is, maybe he's saying "I use malloc", but means "I manually pack the data into unaligned locations instead of using a struct". Actually malloc is not needed in order to manually pack the struct, but failing to realize that is a lesser degree of confusion. It is necessary to define the data layout sent over the wire. Different implementations will pad the data differently when the struct is used.

Steve Jessop
  • 273,490
  • 39
  • 460
  • 699
  • Thanks for the points about alignment. The data in question is a char array, so I suspect it's not an alignment thing here, nor a struct thing -- though that was my first thought too. – hcarver Nov 08 '12 at 12:30
  • 5
    @Hbcdev: well `char` arrays are never padded at all, so I'll stick with "confused" as the explanation. – Steve Jessop Nov 08 '12 at 14:21
5

Your colleague may have had new[]/delete[]'s magic cookie in mind (this is the information the implementation uses when deleting an array). However, this would not have been a problem if the allocation beginning at the address returned by new[] were used (as opposed to the allocator's).

Packing seems more probable. Variations in ABIs could (for example) result in a different number of trailing bytes added at the end a structure (this is influenced by alignment, also consider arrays). With malloc, the position of a structure could be specified and thus more easily portable to a foreign ABI. These variations are normally prevented by specifying alignment and packing of transfer structures.

justin
  • 104,054
  • 14
  • 179
  • 226
  • 2
    This was what I first thought, the "struct is greater than the sum of its parts" problem. Perhaps this is where his idea originally came from. – hcarver Nov 08 '12 at 12:35
3

I think you are right. Padding is done by the compiler not new or malloc. Padding considerations would apply even if you declared an array or struct without using new or malloc at all. In any case while I can see how different implementations of new and malloc could cause problems when porting code between platforms, I completely fail to see how they could cause problems transferring data between platforms.

hcarver
  • 7,126
  • 4
  • 41
  • 67
john
  • 7,897
  • 29
  • 27
  • I'd previously assumed you could consider `new` as a nice wrapper for `malloc` but it seems from other answers that's not _quite_ true. Consensus seems to be that padding should be the same with either; I think the issue with transferring data between platforms only comes if your transfer mechanism is flawed :) – hcarver Nov 08 '12 at 13:03
3

The layout of an object can't depend on whether it was allocated using malloc or new. They both return the same kind of pointer, and when you pass this pointer to other functions they won't know how the object was allocated. sizeof *ptr is just dependent on the declaration of ptr, not how it was assigned.

Barmar
  • 741,623
  • 53
  • 500
  • 612
0

This is my wild guess of where this thing is coming from. As you mentioned, problem is with data transmission over MPI.

Personally, for my complicated data structures that I want to send/receive over MPI, I always implement serialization/deserialization methods that pack/unpack the whole thing into/from an array of chars. Now, due to padding we know that that size of the structure could be larger than the size of its members and thus one also needs to calculate the unpadded size of the data structure so that we know how many bytes are being sent/received.

For instance if you want to send/receive std::vector<Foo> A over MPI with the said technique, it is wrong to assume the size of resulting array of chars is A.size()*sizeof(Foo) in general. In other words, each class that implements serialize/deserialize methods, should also implement a method that reports the size of the array (or better yet store the array in a container). This might become the reason behind a bug. One way or another, however, that has nothing to do with new vs malloc as pointed out in this thread.

hcarver
  • 7,126
  • 4
  • 41
  • 67
mmirzadeh
  • 6,893
  • 8
  • 36
  • 47
  • Copying to char arrays can be problematic -- it's possible some of your cores are on little-endian architectures, and some big-endian (maybe not likely, but possible). You'd have to XDR-encode them or something, but you could just use user-defined MPI datatypes. They easily take account of padding. But I can see what you're saying about the possible cause of misunderstanding -- it's what I'm calling the "struct is greater than the sum of its parts" problem. – hcarver Nov 13 '12 at 21:57
  • Yes, defining MPI data types is another/correct way of doing this. Good point about endianness. Although, I really doubt that would happen on actual clusters. Anyway, I thought if they follow the same strategy, this might lead into bugs ... – mmirzadeh Nov 13 '12 at 23:29
0

When I want to control the layout of my plain old data structure, with MS Visual compilers I use #pragma pack(1). I suppose such a precompiler directive is supported for most compilers, like for example gcc.

This has the consequence of aligning all fields of the structures one behind the other, without empty spaces.

If the platform on the other end does the same ( i.e. compiled its data exchange structure with a padding of 1), then the data retrieved on both side justs fits well. Thus I have never had to to play with malloc in C++.

At worst I would have considered overloading the new operator so as it performs some tricky things, rather than using malloc directly in C++.

hcarver
  • 7,126
  • 4
  • 41
  • 67
Stephane Rolland
  • 38,876
  • 35
  • 121
  • 169
  • What situations are there where you want to control data structure layout? Just curious. – hcarver Nov 20 '12 at 08:49
  • And does anyone know of compilers supporting `pragma pack` or similar? I realise it won't be part of the standard. – hcarver Nov 20 '12 at 08:49
  • gcc supports it for example. in what situation did I need that: sharing binary data between two different plateform: sharing binary stream between windows and palmOS, between windows and linux. links about gcc: http://gcc.gnu.org/onlinedocs/gcc/Structure_002dPacking-Pragmas.html – Stephane Rolland Nov 20 '12 at 10:01
0

In c++: newkeyword is used to allocate some particular bytes of memory with respect to some data-structure. For example, you have defined some class or structure and you want to allocate memory for its object.

myclass *my = new myclass();

or

int *i = new int(2);

But in all cases you need the defined datatype (class, struct, union, int, char etc...) and only that bytes of memory will be allocated which is required for its object/variable. (ie; multiples of that datatype).

But in case of malloc() method, you can allocate any bytes of memory and you don't need to specify the data type at all times. Here you can observe it in few possibilities of malloc():

void *v = malloc(23);

or

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

or

char *c = (char*)malloc(sizeof(char)*35);
Rahul Raina
  • 3,322
  • 25
  • 30
-1

malloc is a type of function and new is a type of data type in c++ in c++, if we use malloc than we must and should use typecast otherwise compiler give you error and if we use new data type for allocation of memory than we no need to typecast

hk_043
  • 15
  • 4
  • 1
    I think you should try to argument your answer a little more. – Carlo Dec 23 '15 at 12:33
  • This doesn't seem to address the question of them doing different things with paddings, which is what I was really asking above. – hcarver Dec 26 '15 at 17:28