Review request for Boost.Align

Source: http://github.com/glenfe/align Documentation: http://glenfe.com/align/doc/ Motivation: Tiny library to be used for all common alignment related chores found in a few Boost libraries. (e.g. we have some in Boost.Smart_Ptr). Provides: 1. Function boost::align, a pointer alignment function, for implementations that do not yet provide std::align. (Uses std::align if available) 2. Functions boost::aligned_alloc and boost::aligned_free, for aligned allocation and deallocation. (Use platform specific functions, such _aligned_malloc or posix_memalign, or otherwise uses std::malloc with the above boost::align) 3. Class template boost::aligned_allocator as a replacement for std::allocator that respects over-aligned types. (Up to the extent the implementation supports over-alignment) Many thanks to Peter Dimov for his feedback on the design and implementation of this library. Notes: Small enough that it might satisfy the requirements for a fast-track review. Glen

On Thu, Feb 20, 2014 at 12:27 PM, Glen Fernandes
Source: http://github.com/glenfe/align
Documentation: http://glenfe.com/align/doc/
Motivation: Tiny library to be used for all common alignment related chores found in a few Boost libraries. (e.g. we have some in Boost.Smart_Ptr).
Provides: 1. Function boost::align, a pointer alignment function, for implementations that do not yet provide std::align. (Uses std::align if available)
2. Functions boost::aligned_alloc and boost::aligned_free, for aligned allocation and deallocation. (Use platform specific functions, such _aligned_malloc or posix_memalign, or otherwise uses std::malloc with the above boost::align)
3. Class template boost::aligned_allocator as a replacement for std::allocator that respects over-aligned types. (Up to the extent the implementation supports over-alignment)
Many thanks to Peter Dimov for his feedback on the design and implementation of this library.
Notes: Small enough that it might satisfy the requirements for a fast-track review.
First, nice to see this submission, I've been missing and reimplementing this functionality from time to time. Second, you may see this: https://github.com/boostorg/log/blob/master/include/boost/log/detail/malloc_... to improve portability of aligned_alloc. As you can see, posix_memalign is not always available, even if advertised by POSIX macros. Note also that posix_memalign imposes specific requirements on the alignment that are not described in your docs. And _aligned_malloc/_aligned_free are available on compilers other than MSVC.

On Thu, Feb 20, 2014 at 12:52 PM, Andrey Semashev
On Thu, Feb 20, 2014 at 12:27 PM, Glen Fernandes
wrote: Source: http://github.com/glenfe/align
Documentation: http://glenfe.com/align/doc/
Motivation: Tiny library to be used for all common alignment related chores found in a few Boost libraries. (e.g. we have some in Boost.Smart_Ptr).
Provides: 1. Function boost::align, a pointer alignment function, for implementations that do not yet provide std::align. (Uses std::align if available)
2. Functions boost::aligned_alloc and boost::aligned_free, for aligned allocation and deallocation. (Use platform specific functions, such _aligned_malloc or posix_memalign, or otherwise uses std::malloc with the above boost::align)
3. Class template boost::aligned_allocator as a replacement for std::allocator that respects over-aligned types. (Up to the extent the implementation supports over-alignment)
Many thanks to Peter Dimov for his feedback on the design and implementation of this library.
Notes: Small enough that it might satisfy the requirements for a fast-track review.
First, nice to see this submission, I've been missing and reimplementing this functionality from time to time.
Second, you may see this:
https://github.com/boostorg/log/blob/master/include/boost/log/detail/malloc_...
to improve portability of aligned_alloc. As you can see, posix_memalign is not always available, even if advertised by POSIX macros. Note also that posix_memalign imposes specific requirements on the alignment that are not described in your docs. And _aligned_malloc/_aligned_free are available on compilers other than MSVC.
One other note. I can see that aligned_allocator does not allow to specify the alignment. I assume it uses the type T alignment? Can this class be extended so that I can specify the alignment explicitly in the template parameters? This would be very useful for cases when the elements of type T must have stronger alignment, for example, when element processing needs to be vectorized.

Andrey Semashev wrote:
First, nice to see this submission, I've been missing and reimplementing this functionality from time to time.
Second, you may see this:
Thanks! I'll extend the implementation and update the documentation around aligned_alloc accordingly.
One other note. I can see that aligned_allocator does not allow to specify the alignment. I assume it uses the type T alignment?
Yes, it uses the type T alignment.
Can this class be extended so that I can specify the alignment explicitly in the template parameters?
Definitely. Instead of specifying the alignment value as a template
parameter, maybe a preferable design is:
template

On Thu, Feb 20, 2014 at 1:21 PM, Glen Fernandes
Can this class be extended so that I can specify the alignment explicitly in the template parameters?
Definitely. Instead of specifying the alignment value as a template parameter, maybe a preferable design is:
template
class aligned_allocator; ...where 'A' could supply different alignment values for different rebound types.
I think it complicates things too much. For example, if I just want to align memory at cache line boundary (64 bytes for Intel CPUs), I would have to create a metafunction just to provide this single constant to the allocator. I can understand why you would want to use a metafunction here - you want rebound allocator to use the proper alignment. But realistically, no one ever wants to align memory weaker than malloc/new, and it is already guaranteed to align properly for any builtin types. So you would specify a stronger alignment than malloc/new provides, which means it should already be strong enough for any T. That's why I think a simple constant would be enough. template< typename T, unsigned int Alignment = 0 > class aligned_allocator { static constexpr unsigned int alignment = Alignment > 0 ? Alignment : alignment_of< T >::value; static_assert(alignment >= alignment_of< T >::value, "Specified alignment is too weak"); }; But if you decide to go with the metafunction, then at least provide a boilerplate so that it is easy to specify a constant.

On Thu, Feb 20, 2014 at 1:55 PM, Andrey Semashev
On Thu, Feb 20, 2014 at 1:21 PM, Glen Fernandes
wrote: Can this class be extended so that I can specify the alignment explicitly in the template parameters?
Definitely. Instead of specifying the alignment value as a template parameter, maybe a preferable design is:
template
class aligned_allocator; ...where 'A' could supply different alignment values for different rebound types.
I think it complicates things too much. For example, if I just want to align memory at cache line boundary (64 bytes for Intel CPUs), I would have to create a metafunction just to provide this single constant to the allocator.
I can understand why you would want to use a metafunction here - you want rebound allocator to use the proper alignment. But realistically, no one ever wants to align memory weaker than malloc/new, and it is already guaranteed to align properly for any builtin types. So you would specify a stronger alignment than malloc/new provides, which means it should already be strong enough for any T. That's why I think a simple constant would be enough.
template< typename T, unsigned int Alignment = 0 > class aligned_allocator { static constexpr unsigned int alignment = Alignment > 0 ? Alignment : alignment_of< T >::value; static_assert(alignment >= alignment_of< T >::value, "Specified alignment is too weak"); };
Or even: template< typename T, unsigned int Alignment = 0 > class aligned_allocator { static constexpr unsigned int alignment = Alignment > alignment_of< T >::value ? Alignment : alignment_of< T >::value; }; This would make it "just work".
But if you decide to go with the metafunction, then at least provide a boilerplate so that it is easy to specify a constant.

-----Message d'origine----- De : Boost [mailto:boost-bounces@lists.boost.org] De la part de Andrey Semashev Envoyé : jeudi 20 février 2014 10:56 À : boost@lists.boost.org Objet : Re: [boost] Review request for Boost.Align
I think it complicates things too much. For example, if I just want to align memory at cache line boundary (64 bytes for Intel CPUs), I would have to create a metafunction just to provide this single constant to the allocator.
I can understand why you would want to use a metafunction here - you want rebound allocator to use the proper alignment. But realistically, no one ever wants to align memory weaker than malloc/new, and it is already guaranteed to align properly for any builtin types. So you would specify a stronger alignment than malloc/new provides, which means it should already be strong enough for any T. That's why I think a simple constant would be enough.
template< typename T, unsigned int Alignment = 0 > class aligned_allocator { static constexpr unsigned int alignment = Alignment > 0 ? Alignment : alignment_of< T >::value; static_assert(alignment >= alignment_of< T >::value, "Specified alignment is too weak"); };
But if you decide to go with the metafunction, then at least provide a boilerplate so that it is easy to specify a constant.
I second Andrey, whatever the type T is, it would be nice to have an easy way of specifying a stronger alignment constraint. This is particularly useful to allocate for example buffers of uint8_t on 16-byte or 64-byte boundaries for performance reasons. Regards, Étienne This message and any attachments are confidential and intended solely for the addressees. Any unauthorized modification, edition, use or dissemination is prohibited. If you have received this message by mistake, please notify us immediately. ATEME decline all responsibility for this message if it has been altered, deformed, falsified or even edited or disseminated without authorization.

DUPUIS Etienne wrote:
I second Andrey, whatever the type T is, it would be nice to have an easy way of specifying a stronger alignment constraint. This is particularly useful to allocate for example buffers of uint8_t on 16-byte or 64-byte boundaries for performance reasons.
You're perhaps missing a certain subtlety here and it is that the
container/algorithm taking the allocator<T> may allocate objects other than
T. It does this via rebind<>. And the question then becomes, do you want
this 64 byte boundary to also apply to these additional allocations? The
answer is often 'no' - you don't want deque<T>'s bookkeeping structures to
be overaligned - but sometimes it's 'yes', if you passed allocator<float>
but the function actually uses allocator<char>. And sometimes, as with
list<T>, the answer is non-binary.
If the required alignment is equal to alignof(T), it all works - structures
having T as a member will automatically receive an alignment at least as
strict, and functions using allocator<char> instead of allocator<T> will
take the necessary steps to std::align the resulting pointer at alignof(T).
So if you allocate T = struct { char[64]; } alignas(64), it would avoid all
these complications. This depends on the compiler providing a proper support
for overaligned types. But that's needed for __m128 and __m256, so I'd
expect it to be there.
Not that aligned_allocator

On Thu, Feb 20, 2014 at 5:35 PM, Peter Dimov
DUPUIS Etienne wrote:
I second Andrey, whatever the type T is, it would be nice to have an easy way of specifying a stronger alignment constraint. This is particularly useful to allocate for example buffers of uint8_t on 16-byte or 64-byte boundaries for performance reasons.
You're perhaps missing a certain subtlety here and it is that the container/algorithm taking the allocator<T> may allocate objects other than T. It does this via rebind<>. And the question then becomes, do you want this 64 byte boundary to also apply to these additional allocations? The answer is often 'no' - you don't want deque<T>'s bookkeeping structures to be overaligned - but sometimes it's 'yes', if you passed allocator<float> but the function actually uses allocator<char>. And sometimes, as with list<T>, the answer is non-binary.
Yes, that is true. However, when I specify alignment in the allocator, what I explicitly care about is alignment of the elements. For the most part I don't care what alignment the container uses for its internal structures, although I realize that this probably affects memory overhead. This can be perceived as a shortcoming of the current containers interface - you can only specify one allocator, the one that is "supposed" to be used for the elements, and the container uses it for other purposes as well behind your back. Luckily, most containers only allocate structures that embed elements, so the alignment is justified. One notable exception is unordered containers - these would have to also allocate the bucket list, which need not be aligned as strict as the elements.
If the required alignment is equal to alignof(T), it all works - structures having T as a member will automatically receive an alignment at least as strict, and functions using allocator<char> instead of allocator<T> will take the necessary steps to std::align the resulting pointer at alignof(T).
Yes.
So if you allocate T = struct { char[64]; } alignas(64), it would avoid all these complications. This depends on the compiler providing a proper support for overaligned types. But that's needed for __m128 and __m256, so I'd expect it to be there.
::allocate(n), you're basically doing new __m128[n], and this does not align memory to 16 bytes. Well, it does on x86_64 Linux/Windows/OS X but simply because all memory allocations are 16-byte aligned on
I think, current implementations (at least, those I have worked with) don't support this. I.e. if you do std::allocator< __m128 that architecture and not because alignas(__m128) == 16. I think, with C++03 this was justified by the fact that __m128 has non-standard alignment, which is not covered by the standard. Not sure if this changed in C++11 with introduction of alignas.
Not that aligned_allocator
would not be useful; it would be, as long as it works. There's no guarantee that it will in all cases though.
Exactly. Basically, when you use the aligned_allocator< __m128 > with a container, you have no guarantee that aligned_allocator< __m128 > will actually be used to allocate memory. I.e. if you want to allocate __m128 elements aligned to 64 bytes and specify: template< typename T > struct my_alignment_of : alignment_of< T > {}; template< > struct my_alignment_of< __m128 > : mpl::int_< 64 > {}; std::list< __m128i, aligned_allocator< __m128, my_alignment_of > > d; this will likely not work because std::list won't use my_alignment_of< __m128 > but instead some my_alignment_of< list_node< __m128 > >. To make this work you'd have to write some fake metafunction that just always returns 64, and this is equivalent to just specifying 64 in aligned_allocator template parameters, only more complicated.

Andrey Semashev wrote:
I think, current implementations (at least, those I have worked with) don't support this. I.e. if you do std::allocator< __m128 >::allocate(n), you're basically doing new __m128[n], and this does not align memory to 16 bytes.
That's not what I had in mind. The use case is aligned_allocator< __m128 >, not std::allocator< __m128 >. When you pass such an allocator, the cases are: * A<__m128> is used. We're fine if alignof(__m128) is correct. * A< something unrelated to __m128 > is used. Fine. * A< char > is used and the result is aligned to alignof(__m128). Fine, if alignof(__m128) is correct. * A< struct with an __m128 member > is used. Fine if alignof(struct) is at least as strict as alignof(__m128) and suitable padding is inserted to align the __m128 member. It is this last requirement - that the implementation should propagate extended alignment upwards into structs and align members properly - that I said was necessary. ...
this will likely not work because std::list won't use my_alignment_of<__m128 > but instead some my_alignment_of< list_node< __m128 > >. To make this work you'd have to write some fake metafunction that just always returns 64, and this is equivalent to just specifying 64 in aligned_allocator template parameters, only more complicated.
Specifying 64 is not going to work for list<> containers. That was my point.
template< class T > struct __list_node { void* next_; T t_; }
If you align the __list_node at 64, the t_ member is not going to be aligned
at 64.
aligned_allocator

On Thu, Feb 20, 2014 at 7:07 PM, Peter Dimov
Andrey Semashev wrote:
this will likely not work because std::list won't use my_alignment_of<__m128 > but instead some my_alignment_of< list_node< __m128
. To make this work you'd have to write some fake metafunction that just always returns 64, and this is equivalent to just specifying 64 in aligned_allocator template parameters, only more complicated.
Specifying 64 is not going to work for list<> containers. That was my point.
template< class T > struct __list_node { void* next_; T t_; }
If you align the __list_node at 64, the t_ member is not going to be aligned at 64.
aligned_allocator
is going to work for vector<> though, and this is often precisely what's needed. So I think that your suggestion of specifying a minimal alignment, with the actual alignment being determined as LCM( min_align, alignof(T) ) is useful, as long as people realize that it's tailored to vector<> and is not generally suitable.
You're right, I forgot about the node layout. I guess, the only way to make sure it works in all cases is to use both the element type with the required alignment and aligned_allocator.

Updated the source, documentation, tests, examples.
Source: https://github.com/glenfe/align
Doc: http://glenfe.com/align/doc/
1. aligned_alloc isn't confined to sizeof(void*) multiples for posix_memalign
2. aligned_alloc supports _aligned_malloc and posix_memalign on more platforms
3. aligned_allocator now allows specifying minimum alignment
template
Why are there no allocator_traits?
In what way? std::allocator_traits is usable with boost::aligned_allocator, just as it is with std::allocator. Concerning the interface of boost::aligned_allocator, it provides the same type traits as the specification of std::allocator in 20.6.9 [default.allocator].
Should there be support for placement new?
What do you suggest? The result of boost::aligned_alloc (or the result of boost::align), given the appropriate alignment, is suitable for use with placement new. We could provide additional utilities that do both allocation and construction. Thanks! Glen

Glen Fernandes wrote:
Updated the source, documentation, tests, examples.
Source: https://github.com/glenfe/align Doc: http://glenfe.com/align/doc/
I think that 'alignment' should be checked: BOOST_ASSERT( alignment & (alignment - 1 ) == 0 ); as the implementation does already assume that it's a power of 2. I don't think that "the value of alignment shall be a valid alignment supported by the implementation" is a valid requirement for us. The standard uses such language because it's not an implementation, but we are the implementation, so we must say what alignments we support. The aligned_ptr example doesn't seem to call ~T. It might be a good idea to document that using aligned_allocator with a minimum alignment is generally only suitable for vector and there may be surprises if such an allocator is passed to a node-based container.

Andrey Semashev wrote:
Note also that posix_memalign imposes specific requirements on the alignment that are not described in your docs.
I'd rather go the other way and round up to a sizeof(void*) multiple in the posix_memalign-based implementation. It makes no sense to me for aligned_alloc to not work for alignment 1, 2, 4 when sizeof(void*) is 8. IOW, it should work for alignof(T) for any T. The posix_memalign-specific requirement should not bubble up.

On Thu, Feb 20, 2014 at 3:26 PM, Peter Dimov
Andrey Semashev wrote:
Note also that posix_memalign imposes specific requirements on the alignment that are not described in your docs.
I'd rather go the other way and round up to a sizeof(void*) multiple in the posix_memalign-based implementation. It makes no sense to me for aligned_alloc to not work for alignment 1, 2, 4 when sizeof(void*) is 8. IOW, it should work for alignof(T) for any T. The posix_memalign-specific requirement should not bubble up.
Agreed.

Please notice that the Boost.SIMD proposal also have a couple of aligned memory management functions. It may be a good idea to align (pun intended) your proposal with these. Why are there no allocator_traits? Should there be support for placement new? I assume that Boost.Align aspires to be a public library, not only to be used internally, so I think that the documentation could be improved with examples. The documentation does not mention how much extra memory is wasted due to alignment.

On 20/02/14 12:41, Bjorn Reese wrote:
Please notice that the Boost.SIMD proposal also have a couple of aligned memory management functions. It may be a good idea to align (pun intended) your proposal with these.
The code is here if you're interested: https://github.com/MetaScale/nt2/tree/master/modules/boost/simd/sdk/include/... It has functions to adapt any memory allocating scheme or C++ allocator into an aligned version. It has a mechanism similar to Boost.Assert where you can globally define your underlying custom memory allocator. It also has functions to make use of system-specific functions, which are used by default.

On Fri, Feb 28, 2014, Mathias Gaunard wrote:
The code is here if you're interested: https://github.com/MetaScale/nt2/tree/master/modules/boost/simd/sdk/include/...
It has functions to adapt any memory allocating scheme or C++ allocator into an aligned version. It has a mechanism similar to Boost.Assert where you can globally define your underlying custom memory allocator.
It also has functions to make use of system-specific functions, which are used by default.
Thanks. Related to the first point, I recently implemented class template boost::aligned_allocator_adaptor to complement boost::aligned_allocator, but I think I could improve the implementation. The mechanism to supporting globally defining a custom allocator is interesting. I'll take a look; thanks. Glen

Hello,
I have recieved your request and will add this library to the review schedule.
Best,
Ron
On 2014-02-20, at 3:27 AM, Glen Fernandes
Source: http://github.com/glenfe/align
Documentation: http://glenfe.com/align/doc/
Motivation: Tiny library to be used for all common alignment related chores found in a few Boost libraries. (e.g. we have some in Boost.Smart_Ptr).
Provides: 1. Function boost::align, a pointer alignment function, for implementations that do not yet provide std::align. (Uses std::align if available)
2. Functions boost::aligned_alloc and boost::aligned_free, for aligned allocation and deallocation. (Use platform specific functions, such _aligned_malloc or posix_memalign, or otherwise uses std::malloc with the above boost::align)
3. Class template boost::aligned_allocator as a replacement for std::allocator that respects over-aligned types. (Up to the extent the implementation supports over-alignment)
Many thanks to Peter Dimov for his feedback on the design and implementation of this library.
Notes: Small enough that it might satisfy the requirements for a fast-track review.
Glen
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

----------------------------------------
From: rxg@cs.cmu.edu Date: Sat, 22 Feb 2014 15:28:47 -0500 To: boost@lists.boost.org Subject: Re: [boost] Review request for Boost.Align
Hello,
I have recieved your request and will add this library to the review schedule.
I'll be the review manager for this (can't be that hard, right).
On 2014-02-20, at 3:27 AM, Glen Fernandes
wrote: Source: http://github.com/glenfe/align
Documentation: http://glenfe.com/align/doc/
Motivation: Tiny library to be used for all common alignment related chores found in a few Boost libraries. (e.g. we have some in Boost.Smart_Ptr).
Provides: 1. Function boost::align, a pointer alignment function, for implementations that do not yet provide std::align. (Uses std::align if available)
2. Functions boost::aligned_alloc and boost::aligned_free, for aligned allocation and deallocation. (Use platform specific functions, such _aligned_malloc or posix_memalign, or otherwise uses std::malloc with the above boost::align)
3. Class template boost::aligned_allocator as a replacement for std::allocator that respects over-aligned types. (Up to the extent the implementation supports over-alignment)
Many thanks to Peter Dimov for his feedback on the design and implementation of this library.
Notes: Small enough that it might satisfy the requirements for a fast-track review.
Glen
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

I'll be the review manager for this (can't be that hard, right).
Ahmed, many thanks. :-)
Some information about the role of a review manager: http://www.boost.org/community/reviews.html#Review_Manager The next step, after you verify the submission code/documentation is complete, would be to get in touch with the review wizard, Ron (rxg@cs.ubc.ca), and finalize a date for the review. Glen

On February 25, 2014 4:31:00 AM EST, Glen Fernandes
I'll be the review manager for this (can't be that hard, right).
Ahmed, many thanks. :-)
Some information about the role of a review manager: http://www.boost.org/community/reviews.html#Review_Manager
The next step, after you verify the submission code/documentation is complete, would be to get in touch with the review wizard, Ron, and finalize a date for the review.
Actually, you need Review Wizard approval as a review manager, and then the rest follows. ___ Rob (Sent from my portable computation engine)
participants (9)
-
Ahmed Charles
-
Andrey Semashev
-
Bjorn Reese
-
DUPUIS Etienne
-
Glen Fernandes
-
Mathias Gaunard
-
Peter Dimov
-
Rob Stewart
-
Ron Garcia