Alignment and the STL in VS 2012/VC11 - c++

I have a vague memory of the STL having trouble with aligned structs (e.g. SIMD vectors placed in a std::vector), unless you specify a custom allocator.
According to this document VS 2012/VC11 has partial support for c++ alignment. Does this mean that the VS STL implementation can handle aligned structs now, without providing a custom allocator?

No. It means that the VC++ compiler supports a method for specifying the required alignment for a type (the __declspec(align(N)) syntax). VC++ has always supported that, and it is basically listed as "partial" because "we have some alignment-related functionality, and it looks better than saying "not supported".
Apart from that, I'm not aware of anything in the C++11 alignment specification which indicates that SIMD vectors in a standard library container is guaranteed to work. C++11 alignment is basically just a formalization of what compilers already did, in this regard (as far as I know. I'd love if you could prove me wrong).
SIMD vectors are what the standard calls "over-aligned types" (see the part about "extended alignment"). What that means is basically "we guarantee nothing, and it's entirely up to the compiler how/if they handle such types.
In other words, implementing this part of C++11 would not necessarily change how SIMD objects are handled.

Related

std::uninitialized_move with specific allocator

I'm writing a custom container template class that, as many (if not all) the containers in the stl, can use a specified allocator type.
To implement the range insert function, I need to move some of the elements in the container a number of spaces forward, where the memory is still uninitialized. To do this I want to use some (nonexistent) version of std::uninitialized_move() that uses the allocator in the container.
The other option is to do the move-construction of the objects using the allocator in a for loop and destroy the constructed objects in case of an exception. That's basically re-implementing std::uninitialized_move() with an extra feature.
The standard library implementation for my compiler (GCC) has exactly the functions I need (std::__uninitialized_move_a(), std::__uninitialized_copy_a(), etc), and are in fact used in the implementation of std containers, but I think those are rather compiler-specific.
Should I use this functions (portability)? Or is there other, more practical, option?
Maybe there is something in the standard library that I'm missing.
Should I use this functions (portability)?
You shouldn't use the GCC internal functions.
No, there doesn't seem to be standard equivalents. You can write your own versions of those functions. If you do, note that CustomAlloc::construct is an optional function (for example, std::allocator doesn't have this function since C++20), so it should be use through std::allocator_traits<CustomAlloc>::construct. This has additional benefit of being constexpr since C++20.
Or is there other, more practical, option?
One option is to ignore the possibility that CustomAlloc::construct has been implemented to do something other than direct placement new, and thereby simply use the standard std::uninitialized_move.
This technically limits the allocators that your container supports in theory, but on the other hand I've never seen such custom allocator used in practice. This may be reasonable limitation if the container is for internal use at least. If you do this, document the behaviour carefully.

C++ Eigen Matrix clarifications

I have only recently started exploring C++ Eigen library and a little puzzled with some of the documentation. It would be great if someone can clarify this.
In the common pitfalls (https://eigen.tuxfamily.org/dox-devel/TopicPitfalls.html) Alignment Issues section, it says " Indeed, since C++17, C++ does not have quite good enough support for explicit data alignment.".
The page on how to get rid of alignment issues (https://eigen.tuxfamily.org/dox-devel/group__TopicUnalignedArrayAssert.html#getrid), the documentation says, "If you can target [c++17] only with a recent compiler (e.g., GCC>=7, clang>=5, MSVC>=19.12), then you're lucky: enabling c++17 should be enough" .
So is alignment not an issue with Eigen Matrix if I am using c++ 17 with gcc>=7.0? Have I understood this right? And that the macro EIGEN_MAKE_ALIGNED_OPERATOR_NEW won't be needed? And if this is correct, what is different between c++14/c++17 which takes care of the alignment issues?
The second question is regarding the pass-by-value section (https://eigen.tuxfamily.org/dox-devel/group__TopicPassingByValue.html). The documentation claims that pass-by-value could be illegal and could crash the program. This is very puzzling to me. Wouldn't pass-by-value just invoke a copy constructor? As an example.
Eigen::Vector3f veca = ComputeVecA();
Eigen::Vector3f vecb = veca; //< If pass-by-value is unsafe, is this operation safe?
And lastly, can I rely on RVO/NRVO for Eigen fixed sized matrix class? I suspect the answer to this is yes.
In the common pitfalls
(https://eigen.tuxfamily.org/dox-devel/TopicPitfalls.html) Alignment
Issues section, it says "Indeed, since C++17, C++ does not have quite
good enough support for explicit data alignment."
This seems to be a typo. It should say "until C++17" instead of "since C++17" because C++17 actually added support for allocation with extraordinary alignment restrictions. Two comments agree with me.
The page on how to get rid of alignment issues
(https://eigen.tuxfamily.org/dox-devel/group__TopicUnalignedArrayAssert.html#getrid),
the documentation says, "If you can target [C++17] only with a recent
compiler (e.g., GCC >= 7, Clang >= 5, MSVC >= 19.12), then you're
lucky: enabling C++17 should be enough."
So is alignment not an issue with Eigen Matrix if I am using C++17
with gcc >= 7.0? Have I understood this right? And that the macro
EIGEN_MAKE_ALIGNED_OPERATOR_NEW won't be needed?
Yes.
And if this is correct, what is different between C++14/C++17 which
takes care of the alignment issues?
C++17 supports Dynamic memory allocation for over-aligned data. operator new now properly allocates over-aligned memory with the align_val_t argument.
The second question is regarding the pass-by-value section
(https://eigen.tuxfamily.org/dox-devel/group__TopicPassingByValue.html).
The documentation claims that pass-by-value could be illegal and could
crash the program. This is very puzzling to me. Wouldn't pass-by-value
just invoke a copy constructor?
If the variable is a local variable (as vecb in your example), then the compiler and the library take care to ensure that vecb meets the special alignment restriction required by Eigen. However, if the variable is a function parameter, this alignment restriction is not respected, meaning the program may operate on ill-aligned memory, thus crash. (This has little to do with the copy constructor.)
And lastly, can I rely on RVO/NRVO for Eigen fixed sized matrix class?
I suspect the answer to this is yes.
The answer is pretty much the same for Eigen classes and other classes: try and see. Usually the answer is yes.
Q1: as already commented, this was a typo when updating this paragraph for c++17. This is already fixed.
Q2: I don't remember all the details about this one but it is related to two technical issues.
Some compilers failed to properly align the stack, in this case it is hopeless to get aligned function parameters.
Old ABI specifications did not allowed overalignment of function parameters.
I would expect that since c++11 and the usage of the standardized alignas keyword this is not an issue anymore, but maybe this is still a problem on some exotic compiler-OS combinations.
Q3: there is nothing preventing RVO/NRVO, and from my experience when it can apply it does apply.

Regarding non-equal allocators in STL

I am reading Item 10 in Effective STL by Scott Meyers on allocators in C++.
Standard says that an implementation of the STL is permitted to assume
that all allocator objects of the same type are equivalent and always
compare equal.
That's all well and good, but the more you think about it. the more
you'll realize just how draconian a restriction it is that STL
implementations may assume that allocators of the same type are
equivalent. It means that portable allocator objects — allocators that
will function correctly under different STL implementations — may not
have state. Let's be explicit about this: it means that portable
allocators may not have any nonstatic data members, at least not any
that affect their behavior. None. Nada. That means, for example, you
can't have one SpecialAllocator that allocates from one heap and
a different SpecialAllocator that allocates from a different
heap. Such allocators wouldn't be equivalent, and STL implementations
exist where attempts to use both allocators could lead to corrupt
runtime data structures.
In fairness to the Standardization Committee, I should point out that
it included the following statement immediately after the text that
permits STL implementers to assume that allocators of the same type
are equivalent:
Implementors are encouraged to supply libraries that ... support
non-equal instances. In such implementations. ... the semantics of
containers and algorithms when allocator instances compare non-equal
are implementation-defined.
This is a lovely sentiment, but as a user of the STL who is
considering the development of a custom allocator with state, it
offers you next to nothing. You can take advantage of this statement
only if (1) you know that the STL implementations you are using
support inequivalent allocators, (2) you are willing to delve into
their documentation to determine whether the implementation-defined
behavior of "non-equal" allocators is acceptable to you, and
(3) you're not concerned about porting your code to STL
implementations that may take advantage of the latitude expressly
extended to them by the Standard. In short, this paragraph — paragraph
5 of section 20.1.5. for those who insist on knowing — is the
Standard's "1 have a dream" speech for allocators. Until that dream
becomes common reality, programmers concerned about portability will
limit themselves to custom allocators with no state.
My question on above paragraph are
What does author mean by inequivalent or non-equal allocators?
What does last paragraph in above text i.e, point 3 mean in simple terms?
That information is out of date. C++11 and later versions support stateful allocators.
The quotes you have posted from Effective C++ are only of concern if you are writing a C++ library which requires custom allocators, does not require C++11, and which supports building against unknown/unspecified standard libraries. To a first approximation, nobody is doing this anymore. The people who were doing it before often had their own "enhanced" standard library implementations to support stateful allocators, such as EASTL or BDESTL.
Two allocators should compare equal if memory allocated by one can be freed by the other. So, for example, an allocator object that allocates from a pool that it holds can allocate and free memory from that pool, but a different allocator object that has a different pool can't (without a great deal of extra bookkeeping) free memory allocated by the first object.
Making that work right was beyond what the standards committee wanted to take on when allocators were first introduced, which is why the words were so squishy. And that license has been revoked in more recent versions of the standard.
The last paragraph means that if you write an allocator whose objects rely on internal state (e.g., the pool allocator that I mentioned above), and the library you're using respects the equality operator (as in, it won't try to pass pointers around among allocators that don't compare equal), your code will break when you try to use it with a different implementation that doesn't pay attention to the equality operator.

should std::vector honour alignof(value_type)?

If I define a simple type with a certain alignment requirement, shouldn't a std::vector<t> of said type honour the alignment for every single element?
Consider the following example
typedef std::array<double,3> alignas(32) avx_point;
std::vector<avx_point> x(10);
assert(!(std::ptrdiff_t(&(x[0]))&31) && // assert that x[0] is 32-byte aligned
!(std::ptrdiff_t(&(x[1]))&31)); // assert that x[1] is 32-byte aligned
I found that the alignment requirement is silently (without any warning) violated by clang 3.2 (with or without -stdlib=libc++), while gcc 4.8.0 issues a warning that it ignores the attributes on the template argument to std::vector (the intel compiler is too daft to understand alignas, but if I use __declspec(align(32)) instead, it behaves like clang). Both create code that triggers the assert.
So, is this correct behaviour or a bug of clang (and icpc) and an issue with gcc?
edit
to answer a question raised in the comments: if I define
typedef typename std::aligned_storage<sizeof (avx_point),
alignof(avx_point)>::type avx_storage;
I get
sizeof (avx_storage) == 32;
alignof(avx_storage) == 32;
but std::vector<avx_storage> still fails to align the first element (and hence all the others too) for clang and gcc (without warning this time). So there are apparently two issues with the implementations: first, that std::allocator<type> ignores any alignment requirements even for the first element (illegal?) and second, that no padding is applied to ensure alignment of subsequent elements.
––––––––––––
edit There is a related, more practical question for how to obtain memory suitably aligned for SSE/AVX operations. In contrast, I want to know whether std::vector<> (or std::allocator<>) shouldn't honour alignas as of the C++ standard (as of 2011). None of the answers to that other question are suitable answers to this one.
first, that std::allocator ignores any alignment requirements even for the first element (illegal?)
I'm far from being an expert on allocators but it seems to me that, unfortunately, this is legal behaviour. More precisely, an allocator might ignore the requested alignment. Indeed, [allocator.requirements], 17.6.3.5/6 states:
If the alignment associated with a specific over-aligned type is not supported by an allocator, instantiation
of the allocator for that type may fail. The allocator also may silently ignore the requested alignment.
You can write your own allocator to give you aligned memory. I've done that before at my work but, unfortunately, for copyright reasons, I cannot disclose the code :-( All I can say, is the obvious thing: it was based on _aligned_malloc and _aligned_free (which are Microsoft extensions). Or you can Google for "aligned allocator" and a few options will come up, one of which is
https://gist.github.com/donny-dont/1471329
I emphasize that I'm not the author of this aligned allocator and I've never used it.
Update
The aligned allocator above is for Visual Studio/Windows but it can be used as a base for implementing aligned allocators on other platforms. You can use the posix memalign family of functions or the C11 function aligned_alloc.
See this post.

STL container library - Is it legal to call allocate/deallocate on different instances of the allocator class?

First of all, I don't think it is. But, I've observed such a behavior with MSVC 10.0 in Debug mode. I'm using a custom allocator class which relies on the user to pass only pointers allocated on the same instance to deallocate. However, in Release mode, my code is working.
Is this a bug or am I mistaken?
The standard requires that any allocator be able to deallocate memory produced by any other allocator of the same type, even if it's a totally different instance. This is required to get list::splice working correctly. It's largely considered a design flaw in the C++ spec, and in C++0x they're introducing a set of fixups to allocators to rememdy this. In the meantime, any allocator you use in the STL containers must not have its own local state.
EDIT: For those of you who want the original language on this, here's §20.1.5/4 of the C++ ISO spec:
Implementations of containers described in this International Standard are permitted to assume that their
Allocator template parameter meets the following two additional requirements beyond those in Table 32.
— All instances of a given allocator type are required to be interchangeable and always compare equal to
each other.
In the latest ISO draft of the C++0x standard, this requirement is no longer present. The default std::allocator will still maintain this invariant as required, but it doesn't look like you'll have to constrain yourself this way in the future.