Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 205
Dancing_bunny Avatar asked Nov 13 '12 23:11

Dancing_bunny


3 Answers

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.

like image 166
Adam Rosenfield Avatar answered Sep 21 '22 10:09

Adam Rosenfield


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.

like image 23
Steve Jessop Avatar answered Sep 21 '22 10:09

Steve Jessop


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.

like image 35
Mark Ransom Avatar answered Sep 18 '22 10:09

Mark Ransom