The future of C++ alignment: passing by value?
Asked Answered
J

2

20

Reading the Eigen library documentation, I noticed that some objects cannot be passed by value. Are there any developments in C++11 or planned developments that will make it safe to pass such objects by value?

Also, why is there no problem with returning such objects by value?

Jago answered 4/4, 2012 at 21:5 Comment(17)
Have you looked at std::aligned_storage<>?Apps
I've never heard of an object that couldn't be passed by value, and would consider such an object poorly designed. I wonder why Eigen did that?Abranchiate
@ildjarn: Now I have :) Could you explain how it affects passing a type by value?Jago
I take issue with Eigen's comment: "Passing objects by value is almost always a very bad idea in C++." Passing objects by value is perfectly reasonable for when you want to, you know, pass it by value.Demob
@Mooing: There are many objects that can't be passed by value, because copying doesn't make sense. How do you write a copy constructor for an exclusive lock?Septennial
@Robᵩ: Considering that Eigen is a matrix library, I suspect that the objects in question are rather heavy, and should be passed by reference whenever possible. There's nothing specifically wrong with disabling copy to eliminate unintentional implicit expensive copies.Septennial
@NeilG : You can place any trivially copyable type inside of the aligned_storage<> object (via aliasing) and pass the aligned_storage<> object by value. The other side of the call would simply need to know how to alias the data. A sensible API would implement those trivially copyable types in terms of aligned_storage<> so the consumer would never need to know about it or do any aliasing manually.Apps
@BenVoigt: Oh right, I hadn't considered noncopyable objects. I also looked at Eigen's documentation, and it makes sense. I can't think of a way to meet their requirements nicely in C++03, not sure it's possible.Abranchiate
@ildjarn: I suspect that "cannot be passed by value" means "non-copyable". So a solution that requires trivial copy is not likely to be helpful.Septennial
@BenVoigt - Fine. Then let them say, "Passing Eigen objects by value is a bad idea." But their blanket statement is just silly.Demob
@Ben : I think they're non-copyable in this case to avoid alignment issues. I.e., it's an artificial limitation.Apps
@Robᵩ: I agree that version of the statement is better. But I haven't seen it in context. Now I have, and you didn't even quote the entire sentence. Shame on you.Septennial
@MooingDuck: What do types have to do with anything? alignas takes a number. alignas(16) means align on 16-byte boundaries. If the compiler cannot do so, the program is ill-formed (diagnostic required). alignas(T) is defined to be alignas(alignof(T)).Pahang
@ildjarn: I checked the spec, I hadn't realized alignas took a number, and didn't realize there was both a alignas and alignof. My bad. Comments deleted.Abranchiate
@Mooing Duck: Eigen uses SSE2 heavily for packed vector and matrix operations which use 16-bit aligned memory for performance. For example, the compiler intrinsic __m128 is 16-bit aligned and cannot be passed by value.Shepp
... this has nothing to do with non-copyable objects, just alignment for 128 byte types.Shepp
@Inverse: does alignas allow you to pass the compiler intrinsic __m128 by value?Jago
P
11

They could do this in C++11:

class alignas(16) Matrix4f
{
    // ...
};

Now the class will always be aligned on a 16-byte boundary.

Also, maybe I'm being silly but this shouldn't be an issue anyway. Given a class like this:

class Matrix4f
{
public:
    // ...
private:
    // their data type (aligned however they decided in that library):
    aligned_data_type data;

    // or in C++11
    alignas(16) float data[16];
};

Compilers are now obligated to allocate a Matrix4f on a 16-byte boundary anyway, because that would break it; the class-level alignas should be redundant. But I've been known to be wrong in the past, somehow.

Pahang answered 4/4, 2012 at 21:23 Comment(6)
Of course, then you also need 16-byte aligned stacks... I think some popular compilers don't respect alignment requirements for local variables and arguments unless you also configure stack alignment.Septennial
@BenVoigt: Ah. :/ Well, at least with alignas they compiler is required to spit out an error saying, "sorry, I can't do that." (Ergo, enforcing passing by reference when truly required.)Pahang
@BenVoigt: If that's the case, then you could never put compiler intrinsics (without special compiler configurations) for SSE or other such things on the stack, since they need 16-byte alignment.Niemi
So, could Eigen be using alignas to solve the problem of objects that can't be passed by value?Jago
@NeilG: As pointed out elsewhere, if there really is a problem passing Eigen objects by value then that's a nasty bug in Eigen and not really a C++ problem per se. But yes, they could use that. (They probably aren't, as compilers in general don't yet suppose alignas.)Pahang
@GManNickG: Sometimes, when performance is paramount (as it is in the kind of code Eigen is intended for), and the language is limited (as it is in C++ prior to C++11), the only acceptable option is to do something dangerous and put a big warning on it. I would not consider this to be a bug in Eigen: it's a design decision, and in my opinion as a user a valid one. The addition of alignment facilities in C++11 is great, but it's an indication of the importance of their prior lack: if libraries could get around these problems then C++11 wouldn't need alignas.Fid
N
19

It is entirely possible that Eigen is just a terribly written library (or just poorly-thought out); just because something is online doesn't make it true. For example:

Passing objects by value is almost always a very bad idea in C++, as this means useless copies, and one should pass them by reference instead.

This is not good advice in general, depending on the object. It is sometimes necessary pre-C++11 (because you might want an object to be uncopyable), but in C++11, it is never necessary. You might still do it, but it is never necessary to always pass a value by reference. You can just move it by value if it contains allocated memory or something. Obviously, if it's a "look-but-don't-touch" sort of thing, const& is fine.

Simple struct objects, presumably like Eigen's Vector2d are probably cheap enough to copy (especially in x86-64, where pointers are 64-bits) that the copy won't mean much in terms of performance. At the same time, it is overhead (theoretically), so if you're in performance critical code, it may help.

Then again, it may not.

The particular crash issue that Eigen seems to be talking about has to do with alignment of objects. However, most C++03 compiler-specific alignment support guarantees that alignment in all cases. So there's no reason that should "make your program crash!". I've never seen an SSE/AltaVec/etc-based library that used compiler-specific alignment declarations that caused crashes with value parameters. And I've used quite a few.

So if they're having some kind of crash problem with this, then I would consider Eigen to be of... dubious merit. Not without further investigation.

Also, if an object is unsafe to pass by value, as the Eigen docs suggest, then the proper way to handle this would be to make the object non-copy-constructable. Copy assignment would be fine, since it requires an already existing object. However, Eigen doesn't do this, which again suggests that the developers missed some of the finer points of API design.

However, for the record, C++11 has the alignas keyword, which is a standard way to declare that an object shall be of a certain alignment.

Also, why is there no problem with returning such objects by value?

Who says that there isn't (noting the copying problem, not the alignment problem)? The difference is that you can't return a temporary value by reference. So they're not doing it because it's not possible.

Niemi answered 4/4, 2012 at 21:22 Comment(8)
Thanks for your answer (+1) — In answer to your question "who says there isn't?" I was merely quoting the bottom of the linked documentation. They say there isn't.Jago
@NeilG: I'd guess that there's no problem with returning objects by value, since the temporary (which may be unaligned or not even exist) can't be used anyway. The values are properly aligned both before and after the return, no matter where (or if) the temporary is.Abranchiate
@MooingDuck: Thanks, that answers the return-value part of my question.Jago
@MooingDuck: VS doesn't support alignas yet, but if GCC supports 16-byte alignment at all (with compiler-specific alignment directives), then it should support 16-byte alignment through alignas. And if the compiler can't do 16-byte alignment, then... it's not possible to even create these things by value safely.Niemi
Worth noting that while VC++ doesn't support alignas yet, it does have __declspec(align(#)), so the semantics are possible, just not yet in a portable syntax.Apps
Several Eigen objects use sse intrinsics member variables like __m128 which can't be passed by value :\Shepp
@Inverse: Then those objects should be non-copyable to make it much more difficult to pass them by value. Also, what is it about these intrinsics that makes them impossible to pass by value?Niemi
Considering the problem doesn't exist for non-passed objects, making it non-copyable would make the library un-usable. I think even the copy-constructors are necessary for usability sake with all the templated types being passed around that need automatic conversion. I don't think they were in-cautious when designing their API, I think they looked at the trade offs and then made the decision anyway. The do offer a define flag to disable alignment but AFAIK it breaks ABI compatibility between code compile with and without.Tijuanatike
P
11

They could do this in C++11:

class alignas(16) Matrix4f
{
    // ...
};

Now the class will always be aligned on a 16-byte boundary.

Also, maybe I'm being silly but this shouldn't be an issue anyway. Given a class like this:

class Matrix4f
{
public:
    // ...
private:
    // their data type (aligned however they decided in that library):
    aligned_data_type data;

    // or in C++11
    alignas(16) float data[16];
};

Compilers are now obligated to allocate a Matrix4f on a 16-byte boundary anyway, because that would break it; the class-level alignas should be redundant. But I've been known to be wrong in the past, somehow.

Pahang answered 4/4, 2012 at 21:23 Comment(6)
Of course, then you also need 16-byte aligned stacks... I think some popular compilers don't respect alignment requirements for local variables and arguments unless you also configure stack alignment.Septennial
@BenVoigt: Ah. :/ Well, at least with alignas they compiler is required to spit out an error saying, "sorry, I can't do that." (Ergo, enforcing passing by reference when truly required.)Pahang
@BenVoigt: If that's the case, then you could never put compiler intrinsics (without special compiler configurations) for SSE or other such things on the stack, since they need 16-byte alignment.Niemi
So, could Eigen be using alignas to solve the problem of objects that can't be passed by value?Jago
@NeilG: As pointed out elsewhere, if there really is a problem passing Eigen objects by value then that's a nasty bug in Eigen and not really a C++ problem per se. But yes, they could use that. (They probably aren't, as compilers in general don't yet suppose alignas.)Pahang
@GManNickG: Sometimes, when performance is paramount (as it is in the kind of code Eigen is intended for), and the language is limited (as it is in C++ prior to C++11), the only acceptable option is to do something dangerous and put a big warning on it. I would not consider this to be a bug in Eigen: it's a design decision, and in my opinion as a user a valid one. The addition of alignment facilities in C++11 is great, but it's an indication of the importance of their prior lack: if libraries could get around these problems then C++11 wouldn't need alignas.Fid

© 2022 - 2024 — McMap. All rights reserved.