Dancing_bunny
Dancing_bunny

Reputation: 135

Is C++ memory alignment correct or inefficient?

I tested this code just trying to find out how much memory c++ actually reserved for the new operator.

#include<iostream>
using namespace std;
int main() {

  cout << "alignment of " << alignof(int) << endl;
  int *intP1 = new int;
  *intP1 = 100;
  cout << "address of intP1 " << intP1 << endl;
  int *intP2 = new int;
  *intP2 = 999;
  cout << "address of intP2 " << intP2 << endl;
  int *intP3 = new int;
  cout << "address of intP3 " << intP3 << endl;
  *intP3 = 333;

  cout << endl;
  cout << (reinterpret_cast<char *>(intP3)-reinterpret_cast<char *>(intP2)) << endl;
  cout << intP3-intP2 << endl;
  cout << endl;

  cout << *(intP1) << endl;
  cout << *(intP1+4) << endl;
  cout << *(intP1+8) << endl;
  cout << *(intP1+16) << endl;
  delete intP1;
  delete intP2;
  delete intP3;
  return 0;
}

After compiled the code with -std=c++11 flag and ran it, here is what I got from a x86_64 machine.

    alignment of int4
    address of intP1 = 0xa59010
    address of intP2 = 0xa59030
    address of intP3 = 0xa59050

    the distance of intP3 and intP2 = 32

    intP1 value = 100
    is this a padding value = 0
    intP2 value = 999
    intP3 value = 333

It seems that when using new to allocate a 4 bytes memory for an integer, it actually reserved 32 bytes block which is the total space for 8 integers. According to the explanation of the c++ alignment, for 64 bit machine, memory is aligned on 16 bytes, why the distance here is 32 bytes?

Could some one help me to sort this out? Thanks in advance.

Upvotes: 6

Views: 1179

Answers (7)

Steve Jessop
Steve Jessop

Reputation: 279225

The 32-byte difference is not just for alignment. Indeed, observe that the address 0xa59010 is not 32-aligned, it's only 16-aligned. So the alignment of your addresses would not be any worse if they were only 16 bytes apart rather than 32.

Rather, the 32 byte difference is an overhead/inefficiency of the memory allocator. I suspect that the allocator:

  • is helpfully giving you 16-aligned addresses. This is what you need for 128-bit SSE types, so it's useful to you, but I don't know whether that's the main reason the allocator is 16-aligning, or whether it's just convenient for the allocator.
  • requires some space "before" the allocation for book-keeping information, which might be 16 bytes (2 pointers or a pointer and a size), but even if not it's rounded up to 16 bytes.
  • only requires 4 bytes for your actual data, but because of the 16 bytes of book-keeping and the 16 byte alignment, the minimum distance between allocations is 32 bytes. So there are 12 bytes of "slack space" / "internal fragmentation" / "waste" when you make a 4 byte allocation.

But that's just a guess, I haven't looked into whatever allocator you're using.

Upvotes: 2

Nicholas Frechette
Nicholas Frechette

Reputation: 366

The C++ standard makes no guarantees on how much overhead each heap allocation has. Regardless of alignment, the allocator typically adds extra overhead. It is very common for allocators to fulfill small allocations out of pre-sized buckets. Here it appears that the smallest bucket is 32 bytes per allocation which isn't unusual. You will seldom find allocators in the wild with buckets smaller than 16 bytes.

If you were to allocate more than 1 int, say int[2] you would probably notice that the memory size taken is identical: 32 bytes.

Note also that there are no guarantees from the C++ standard or allocators that 2 allocations of the same size be contiguous. This may be respected most of the time but should not be relied on.

Upvotes: 0

Peter
Peter

Reputation: 7324

Absolutely nothing requires the operating system to allocate the three pointers intP1, intP2 and intP3 adjacent to one another. Your code may be detecting overhead in the allocations (and of course it's a reasonable assumption that there is some) but it's not sufficient to prove that the spacing is necessarily all allocator overhead.

Upvotes: 0

Mark Ransom
Mark Ransom

Reputation: 308111

Debug versions of new can add quite a bit of padding to give guard space, so that some heap corruptions can be detected. You should run it with both debug and release builds to see if there's a difference.

Upvotes: 1

Konstantin Dinev
Konstantin Dinev

Reputation: 34895

int4 means it occupies 4 bytes, not 4 bits. Everything that the compiler shows you is actually accurate! Here is some documentation on primitives and what the int primitive means.

Here is a tutorial on how to define a 4 bit integer.

Let me just mention that alignof is architecture dependent. In some architectures int means 16 bit int or 2 bytes instead of 4.

Upvotes: 0

Adam Rosenfield
Adam Rosenfield

Reputation: 400174

It has nothing to do with alignment -- it's extra overhead of how the internal memory allocator works. Typically, each memory block has extra hidden information in it at the front and/or back used for maintaining the heap's structure. Exactly how much overhead there is is will vary from platform to platform and from implementation to implementation.

For example, Doug Lea's malloc has an extra overhead of 4-8 bytes per allocation (32-bit pointers) or 8-16 bytes (64-bit pointers) and a minimum allocation size of 16 bytes (32-bit) or 32 bytes (64-bit). That means for even 1-byte allocations, the memory allocator requires a total of 16 bytes of tracking overhead.

Upvotes: 5

PearsonArtPhoto
PearsonArtPhoto

Reputation: 39698

You have both a pointer and a memory value. This allocates 2 memory blocks, which you already stated are 16 bytes a piece. 2x16=32.

I believe this will give you the result you were looking for.

  cout << "alignment of " << alignof(int) << endl;
  int intP1 = 100;
  cout << "address of intP1 " << &intP1 << endl;
  int intP2 = 999;
  cout << "address of intP2 " << &intP2 << endl;
  int intP3 = 333;
  cout << "address of intP3 " << &intP3 << endl;

Upvotes: 0

Related Questions