@safe containers with std.experimental.allocator

bitwise via Digitalmars-d digitalmars-d at puremagic.com
Sat Jan 21 14:44:49 PST 2017


On Saturday, 21 January 2017 at 17:26:35 UTC, Andrei Alexandrescu 
wrote:
> On 01/21/2017 12:14 PM, bitwise wrote:
>> On Saturday, 21 January 2017 at 16:28:16 UTC, Andrei 
>> Alexandrescu wrote:
>>> alignedAllocate provides access to OS/clib-provided 
>>> primitives for
>>> aligned allocation. Those don't require a special deallocation
>>> function, see e.g.
>>> http://en.cppreference.com/w/c/memory/aligned_alloc. -- Andrei
>>
>> This makes sense, but then shouldn't alignedAllocate() be a 
>> free
>> function that could be used to make an aligned allocator?
>
> You mean an aligned allocation? But then the actual method, and 
> whether it is supported at all or not, depends on the allocator.
>
>> Again, the point of an allocator is to provide a standard 
>> interface for
>> memory allocation. The user of which shouldn't have to know 
>> how that
>> memory was allocated, and I can't think of a case where this 
>> would be
>> desired either.
>
> I don't understand this. It's possible there's a confusion at a 
> different level, and we're discussing the consequences.
>
> To reboot:
>
> * Some applications need memory aligned at special powers of 
> two, most are okay with more generic alignments such as 
> word-level. See the "alignment" enum that each allocator must 
> define.
>
> * Some allocators do offer allocations at unusual powers of two 
> by means of alignedAllocate. Don't call it naively! Only 
> applications that need such things (e.g. page-aligned, 
> cache-line-aligned, etc) should need those.
>
> * Those allocators must be able to deallocate memory allocated 
> with allocate() or alignedAllocate() with a single primitive 
> deallocate().
>
>
> Andrei

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.

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

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




More information about the Digitalmars-d mailing list