View Full Version : C++ Byte Alignment
dega512
10-26-2006, 10:32 AM
Is there a way to tell the new operator how to align the memory it allocates?
i.e.
unsigned int __attribute__((aligned(16))) myArray[262144];
unsigned int *myDynamicArray = new unsigned int[262144]; // can i align this somehow?
Thanks!
- dega
Reedbeta
10-26-2006, 11:44 AM
I don't think you can do that with new directly in a portable way. If you're using a microsoft compiler, there are functions _aligned_malloc (http://msdn2.microsoft.com/en-US/library/8z34s9c6.aspx) and _aligned_free (http://msdn2.microsoft.com/en-us/library/17b5h8td.aspx) that will do the job. (You can override the new and delete operators on a class to call these functions if you like.) For g++ I'm sure there's something equivalent though I don't know what it's called.
dave_
10-26-2006, 11:54 AM
hmm good question... Correct me if I'm wrong I dont think its part of the C++ language. In your case I'd guess something like this:
typedef int my_aligned_int __attribute__ ((aligned (16));
unsigned int *myDynamicArray = new unsigned my_aligned_int[262144];
Or perhaps using aligned_malloc, or alternatively roll your own memory management that is always aligned to particular boundaries.
If you do use aligned_malloc for c++ classes you'll need to override new and delete operators to ensure the constructor/destructor is called.
monjardin
10-26-2006, 12:39 PM
You could allocate an area bigger than you need by the unit you want aligned. Then take a look at the address and start at an evenly divisible offset.
pater
10-26-2006, 01:47 PM
You could allocate an area bigger than you need by the unit you want aligned. Then take a look at the address and start at an evenly divisible offset.
I'd suggest the same. Don't forget to keep the original pointer and call delete[] using that one (and not your modified, aligned ptr).
juhnu
10-26-2006, 07:07 PM
std::vector< float, AlignedMemoryAllocator<int, 16, 32> > alignedTo16Bytes;
std::vector< float, AlignedMemoryAllocator<int, 64, 128> > alignedTo64Bytes;
#include <malloc.h>
template<class T, int TALIGN, int TBLOCKSIZE> class AlignedMemoryAllocator: public std::_Allocator_base<T> {
public:
typedef std::_Allocator_base<T> _Mybase;
typedef typename _Mybase::value_type value_type;
typedef value_type _FARQ *pointer;
typedef value_type _FARQ& reference;
typedef const value_type _FARQ *const_pointer;
typedef const value_type _FARQ& const_reference;
typedef _SIZT size_type;
typedef _PDFT difference_type;
template<class _Other> struct rebind { // convert an AlignedMemoryAllocator<T> to an AlignedMemoryAllocator <_Other>
typedef AlignedMemoryAllocator<_Other, TALIGN, TBLOCKSIZE> other;
};
pointer address(reference _Val) const
{ // return address of mutable _Val
return (&_Val);
}
const_pointer address(const_reference _Val) const
{ // return address of nonmutable _Val
return (&_Val);
}
AlignedMemoryAllocator()
{ // construct default AlignedMemoryAllocator (do nothing)
}
AlignedMemoryAllocator(const AlignedMemoryAllocator<T, TALIGN, TBLOCKSIZE>&)
{ // construct by copying (do nothing)
}
template<class _Other>
AlignedMemoryAllocator(const AlignedMemoryAllocator<_Other, TALIGN, TBLOCKSIZE>&)
{ // construct from a related AlignedMemoryAllocator (do nothing)
}
template<class _Other>
AlignedMemoryAllocator<T, TALIGN, TBLOCKSIZE>& operator=(const AlignedMemoryAllocator<_Other, TALIGN, TBLOCKSIZE>&)
{ // assign from a related AlignedMemoryAllocator (do nothing)
return (*this);
}
void deallocate(pointer _Ptr, size_type)
{ // deallocate object at _Ptr, ignore size
if (_Ptr) _aligned_free(_Ptr);
}
pointer allocate(size_type _Count)
{ // allocate array of _Count elements
return allocate(_Count, 0);
}
pointer allocate(size_type _Count, const void _FARQ *hint)
{ // allocate array of _Count elements, ignore hint
size_t byteCount=_Count * sizeof(T);
size_t byteCountLeft = byteCount % TBLOCKSIZE;
if(byteCountLeft) byteCount += TBLOCKSIZE - byteCountLeft;
pointer p = reinterpret_cast<pointer>(_aligned_realloc((void*)hint,byteCount,TALIGN));
//if (!p) throw std::bad_alloc("bad allocation in the custom aligned allocator";
return p;
}
void construct(pointer _Ptr, const T& _Val)
{ // construct object at _Ptr with value _Val
new(_Ptr) T(_Val);
}
void destroy(pointer _Ptr)
{ // destroy object at _Ptr
_Ptr->~T();
}
_SIZT max_size() const
{ // estimate maximum array size
_SIZT _Count = (_SIZT)(-1) / sizeof (T);
return (0 < _Count ? _Count : 1);
}
};
}
If C-style allocations is all you need you can do it like this:
void *malloc16(int size)
{
char *block = new char[size + 16];
int *aligned = (int*)((int)(block + 20) & 0xFFFFFFF0);
aligned[-1] = (int)block;
return aligned;
}
void free16(void *memory)
{
delete ((int**)memory)[-1];
}
dega512
10-27-2006, 10:03 AM
Thanks everyone!
3.2.2.7 Allocating Aligned Memory Blocks (http://www.gnu.org/software/libc/manual/html_node/Aligned-Memory-Blocks.html#Aligned-Memory-Blocks)
.oisyn
10-28-2006, 06:46 AM
If C-style allocations is all you need you can do it like this:
If you add 20 to the allocation pointer you'd better make sure you also allocate 20 extra bytes instead of 16 :). And 19 would be enough btw
If you add 20 to the allocation pointer you'd better make sure you also allocate 20 extra bytes instead of 16 :). And 19 would be enough btw
Hey leave some of the memory corruption fun to the rest. ;)
I just wrote it down from the top of my head. But it's a great lesson to never ever copy-paste code and start using it without verifying it yourself. :excl:
.oisyn
10-28-2006, 05:48 PM
I was expecting a defense like "memory allocations are usually at least 4 byte aligned anyway, so the 16 is good enough", which would indeed be valid if you had added 19 instead of 20 ;)
While fixing it, you could also make it work where sizeof(intptr_t) != 4 like assumed.
vBulletin, Copyright ©2000-2010, Jelsoft Enterprises Ltd.