On Saturday, 21 January 2017 at 23:24:52 UTC, Andrei Alexandrescu wrote:
On 1/21/17 5:44 PM, bitwise wrote:
About alignedMalloc:

In C++ for example, I may want to use a vector full some SIMD type:

class alignas(16) Vec4 {
    union {
        struct { float x, y, z, w; };
        __m128 m;
    };
};

std::vector<Vec4> points = { ... };

In C++ however, 'new' does not respect over-alignment:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0035r2.html

Even if new respected alignment, there is no gauruntee all containers, STL or otherwise, would use 'new' as opposed to malloc by default(maybe
one day?)

So I use a custom aligned allocator:

template <class T, int ALIGN>
class AlignedAllocator {
    T* allocate(size_type n) {
        return (T*)_aligned_malloc(ALIGN, n * sizeof(T));
    }
};

SIMD operations(aligned load and store) can now safely be used on the
contents of the std::vector<Vec4>.

std::vector knows nothing about the alignment of the memory it uses. It only knows to call allocate() of whatever allocator it's given. If I had an allocator with a function 'alignedAllocate' it wouldn't do any good. I believe this is the _correct_ design, and that a container _shouldn't_ have to know about where from, or what kind of memory it's getting.

I understand. That's a questionable design. It only works by virtue of a long-distance convention between the rigged allocator and the element type of the vector.

I don't understand what's questionable about it. I don't see how abstracting the alignment away from the consumer of an allocator is a bad thing.

Considering the above use case, alignedAllocate() is redundant, and
possibly confusing.

Well, you just made use of it in the rigged allocator.

I made use of what I would expect to be a non-member helper function. I'm saying that I don't believe alignedAllocate() should be a part of the standard interface of an allocator, and that allocators should be specialized such that allocate() returns memory with whatever alignment is needed.


About missing alignedDeallocate:

while aligned_alloc(), which works in combination with regular 'free()', is supposed to be standard as of C++11, it's still not supported in
visual studio 2015. Instead, one must use _aligned_malloc, and
_aligned_free. Passing memory from _aligned_malloc to the regular version of free() causes a crash. Thus, different deallocation methods are needed for both. Also, there's homegrown aligned_allocate functions like the following, which require special deallocation functions because
of the exta metadata prepended to the memory:
https://github.com/dlang/phobos/blob/366f6e4e66abe96bca9fd69d03042e08f787d040/std/experimental/allocator/mallocator.d#L134-L134


I suppose you could use aligned allocation for _all_ allocations, even allocations with default alignment, but that would add extra metadata(at
least 8 bytes) to _all_ allocations even when its unnecessary.

So a solution could be to include the alignment as a template parameter of Mallocator, or provide an second AlignedMallocator(uint). The allocate() function of either option would return aligned memory if the 'alignment' template parameter was non-default. Then, the idea of memory alignment would be abstracted away from the containers themselves.

struct Mallocator(uint alignment = platformAlignment){}){}
or
struct AlignedMallocator(uint alignment = platformAlignment){}){}

It seems a matter of time until aligned_alloc gets implemented on Windows.

But how much time? Visual studio always lags behind in standards conformance.

Also, there is still the fact that some may need to use home-grown aligned allocation functions like the ones I linked above that prepend metadata to the memory returned, in which case they will need specialized deallocation functions.

Reply via email to