Jump to content


C++ Byte Alignment


  • You cannot reply to this topic
12 replies to this topic

#1 dega512

    Valued Member

  • Members
  • PipPipPip
  • 108 posts

Posted 26 October 2006 - 05:32 PM

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

#2 Reedbeta

    DevMaster Staff

  • Administrators
  • 4979 posts
  • LocationBellevue, WA

Posted 26 October 2006 - 06:44 PM

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 and _aligned_free 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.
reedbeta.com - developer blog, OpenGL demos, and other projects

#3 dave_

    Senior Member

  • Members
  • PipPipPipPip
  • 584 posts

Posted 26 October 2006 - 06:54 PM

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.

#4 monjardin

    Senior Member

  • Members
  • PipPipPipPip
  • 1033 posts

Posted 26 October 2006 - 07: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.
monjardin's JwN Meter (1,2,3,4,5,6):
|----|----|----|----|----|----|----|----|----|----|
*

#5 pater

    Valued Member

  • Members
  • PipPipPip
  • 117 posts

Posted 26 October 2006 - 08:47 PM

monjardin said:

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).

#6 juhnu

    Valued Member

  • Members
  • PipPipPip
  • 292 posts

Posted 27 October 2006 - 02:07 AM

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);
		}
	};
}



#7 Nick

    Senior Member

  • Members
  • PipPipPipPip
  • 1225 posts

Posted 27 October 2006 - 04:49 PM

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];

}



#8 dega512

    Valued Member

  • Members
  • PipPipPip
  • 108 posts

Posted 27 October 2006 - 05:03 PM

Thanks everyone!

#9 tbp

    Valued Member

  • Members
  • PipPipPip
  • 135 posts

Posted 28 October 2006 - 09:17 AM

3.2.2.7 Allocating Aligned Memory Blocks

#10 .oisyn

    DevMaster Staff

  • Moderators
  • 1822 posts

Posted 28 October 2006 - 01:46 PM

Nick said:

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
C++ addict
-
Currently working on: the 3D engine for Tomb Raider.

#11 Nick

    Senior Member

  • Members
  • PipPipPipPip
  • 1225 posts

Posted 28 October 2006 - 07:36 PM

.oisyn said:

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:

#12 .oisyn

    DevMaster Staff

  • Moderators
  • 1822 posts

Posted 29 October 2006 - 12:48 AM

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 ;)
C++ addict
-
Currently working on: the 3D engine for Tomb Raider.

#13 tbp

    Valued Member

  • Members
  • PipPipPip
  • 135 posts

Posted 29 October 2006 - 01:35 AM

While fixing it, you could also make it work where sizeof(intptr_t) != 4 like assumed.





1 user(s) are reading this topic

0 members, 1 guests, 0 anonymous users