Re: [eigen] Taking advantage of C++11 features in Eigen

[ Thread Index | Date Index | More lists.tuxfamily.org/eigen Archives ]


Hi,

Gaels mail on this topic is in my opinion a quite good summary. I think that the integration of the following C++11 features makes sense:

  - initializer lists
  - move construction and assignment (e.g. sparse is still missing)
  - noexcept (maybe useful in the future)
  
The reason of this limitation is primarily that most of the other features require redundant code. Basically two code paths for C++03 and C++11 and this is a maintenance horror. :) AFAIK, boost and Qt also only use a subset of C++11 features. I am still open to discuss other extensions somebody supposes to be useful as well. I just mentioned the most obvious features.
  
POD improvements (not C++11 related) for vector types are tricky since you would have to specialize the whole Matrix class.. A POD must not have user-defined copy assignment, no user defined destructor and no static members. At the moment, all of this is false for vector types. Other than that, std::copy already implements optimizations for POD types (at least in the newer library implementations even in MSVC).

Regards,
Hauke


On Thu, Feb 13, 2014 at 11:09 AM, Jason Newton <nevion@xxxxxxxxx> wrote:
I'll take this opportunity to mention I've been using Eigen for ~4 years now I've been using C++11 since gcc 4.7 went stable and generally it's been worth while over all.  There were various things that somehow went unfinished in both the standard and in compilers but the shortcomings I know of are to be addressed in C++14 and compilers are doing a pretty good job now adays.  Some of the missing tidbits can still be implemented on top C++11 and various snippets float around the internet like make_array, sister to make_tuple.  First class arrays and initializer lists has proved to be something worthy to adopt in everyday code.

Things that have been greatly simplified but could still be made a bit easier is all the type traits library and helpers for SINFAE.  Simply speaking them + auto/decltype/val are a god-sent for the C++ meta coder.  Unfortunately SFINAE is still a bit roundabout but it is cleaner and can be done more straightforward than in C++03... this is important to eigen as boilerplate for SFINAE makes it very difficult to jump into eigen I've found when I first had to add stuff to it. C++14 and static-if should finally simplify this but we are ways out for that.

I'm also a regular user of fixed size matrices/vectors in structures but boy with (recursive) alignment conditions of nested structures this can be painful and frequent source of grief if one is not careful!  Unfortunately compilers are just now starting to work with alignas as intended  for dynamic allocation so it's still a troublesome topic.

By the way, while I've done this in my own helpers for eigen, it may be worth having a few additional (nested) namespaces for typedef'd eigen common types.  Examples are NotAligned and RowMajor(Storage) / ColMajor(Storage) and combinations thereof so the user can avoid having to do typedefs themselves for say Vector3/4 and really make it obvious what storage traits they're working with when trading data back and forth to other libraries/serializing.  Having an explicit guarantee or way to statically access the offset of storage of a matrix at compile time would also be very helpful - things like HDF5 use offsetof to get this information.

-Jason

On Thu, Feb 13, 2014 at 1:25 AM, Gael Guennebaud <gael.guennebaud@xxxxxxxxx> wrote:

Regarding alignment, I'm afraid the C++11 alignas does not bring any improvement since, AFAIK the practical difficulties are the same as with the respective compiler extensions (http://eigen.tuxfamily.org/dox/group__DenseMatrixManipulation__Alignement.html)

cheers,
gael



On Thu, Feb 13, 2014 at 4:58 AM, Nicola Gigante <nicola.gigante@xxxxxxxxx> wrote:

Il giorno 13/feb/2014, alle ore 02:05, Christoph Hertzberg <chtz@xxxxxxxxxxxxxxxxxxxxxxxx> ha scritto:

>
> Yes, this is only in the devel-branch and it lacks doxygen documentation. Unfortunately, especially for internal things of Eigen you need to study the source code (this has been pending for a while http://eigen.tuxfamily.org/bz/show_bug.cgi?id=138 …)

I definitely wanted to study the source code anyway, so it’s not a problem :)

> Okay, maybe my concern is a bit far-fetched. Consider this scenario:
> A user has a compiler which enables C++11 (or his build system accidentally does). He writes a library which depends on Eigen and claims to be C++03 compatible, since Eigen is and he did not knowingly use C++11 features himself. He then releases his library and gets complaints from users without or with insufficient C++11 support.
>
> Even when only using the code by oneself, I myself had my experiences with compiling on different machines having different compiler versions (actually, my problems were more on different versions of an OS-provided library, but I think this somewhat similar).
>
> Generally, this is not an argument against introducing C++11 features in Eigen, but to properly protecting against "accidentally" using them. I.e., if a user makes a well-thought decision to switch to C++11 he shall be able to do so (and profit from it), but not complain if he later needs to port his code to a system with inferior compiler support.

I personally think that anybody who will want to claim its work to be C++03 compatible (or $anystandard-compatible, by the way)
would at least need to know what this standard supports and what not. Since you support Eigen on a wide variety of compilers, you know
better than me that such a claim would anyway require intensive test effort, taking into account compilers quirks and differences that
in an ideal world should not exist. For such a scenario, accidentally using a feature from another version of the standard is a very
unlikely event, and would imply the existence of some serious problems in the engineering process of said project as a whole…
Note that this is different from having to take into account differences in how compilers implement the _same_ standard, where as said,
compatibility problems can be dealt only with extensive testing...

By the way, brace-init syntax is the only “user visible” change, i.e. the only that would cause some user code to compile
in one standard and not in the other. But compilation failures are not the worst kind of regressions. The worst are performance
regressions. As I said in the previous mail, deciding to use C++11, especially in new projects, means making entirely different design
decision, especially in generic APIs.

Consider for example move semantics. It seems like a mere performance improvement for existing code, like in this example:

std::vector<std::string> v = /*fill somehow..*/;
std::sort(v.begin(), v.end());

As everybody knows, for a big vector and big strings, useless copies in C++03 make this code very very inefficient.
You can easily see 10x speed improvement by just adding -std=c++11 on the command line (supposing the library has support too,
of course).
But is it really only a performance improvement, even if so huge? It’s not. It’s also a _usability_ improvement, because now users can
_really_ write code like that to sort a vector of strings, not only in a benchmark, while before you probably didn’t want to have a vector of
strings in the first place… As in this example, move semantics and other C++11 features really change recommended coding styles and
design patterns (think about the “never say new/delete” rule, for example), it’s not just a matter of more convenient syntax.

To make it “short", my point is that you already have such game-changer features silently enabled in Eigen, since you have move semantics,
because your users can start writing code that return huge matrices by value from functions and never care about performance bottlenecks,
while such code would explode at runtime if compiled in C++03 mode, while still compiling well. If you’re worried about regressions for
someone back-porting code to C++03, this kind of regressions are far worse than compilation errors, especially if an entire code-base
is using an API that need to be redesigned (again, someone facing those troubles “accidentally” should not have been starting
his project in the first place and Eigen support for this or that feature is certainly not who is to blame).

Then, for coherence to your concern, you should thus avoid all C++11 features all-together, not only “user-visible” changes like initializer
lists, and simply forbid users from taking advantage of them, which is clearly not what you want…

To return to brace-init syntax, you already have it “for free” for some fixed sized vectors, since Matrix has a couple of constructors
that take two/three/four scalars. So you can already do something like:

Vector3f v = {1,2,3};

Some user is going to write code like this and wonder why it can’t work for VectorXf as well...

>
>> You can make the choice even more explicit if you want, by testing a EIGEN_USE_CPP11 symbol or something like that.
>
> We decided against further user provided preprocessor symbols (we had some troubles with EIGEN_DEFAULT_TO_ROW_MAJOR in the past http://eigen.tuxfamily.org/bz/show_bug.cgi?id=422 -- again, maybe I'm over-concerned here ...)
>
>> What other libraries are doing is to conditionally support such features if explicitly enabled and supported by the compiler,
>> and silently drop them if missing. From the users point of view is clean, painless, and enables them to choose how to write their code.
>
> Basically, that is the plan for Eigen as well (as far as I interpreted our previous discussions about it). Only we decided to enable features by including files from <Eigen/CXX11/...> instead of using preprocessor defines. If you have good arguments for the latter approach, it would not be too late yet to change that decision (so far we mostly have one unsupported module depending on C++11).
>

My (not so radical as it seems) proposal is to use neither separate headers nor preprocessors defines. What I’d suggest is to simply support
selected features if enabled, and silently drop them if not, as you’re already doing for move semantics and static_assert.
This is what other (also huge) libraries are doing (see boost and Qt for example) and IMHO is the best way to go, from both
the user and code maintenance points of view.

A different thing are modules that _depend_ on C++11, like the one you mentioned. Those can indeed be put in a distinct subdirectory,
or just ifdef everything away if needed features are not available, becoming a big static_assert saying to the user what’s going on.

>> If yours is not a firm stop sign, I would like to start working on some ideas and, as time goes, we can see if they're worth or not.
>
> I definitely don't want to stop you entirely from providing C++11 features to Eigen (nor would I consider to have anything near the right to solitarily keep you from doing it).
> I guess I mostly was a bit worried by your first mail that your plans would endanger C++03 compatibility/maintainability ...
>
> Sorry for skipping most of your mail. Generally, I agree that C++11 adds a lot of very useful extensions, which we eventually shall use in Eigen. For details, maybe C++11 experts want to join the discussion …
>

Of course C++03 compatibility must remain an axiom, and maybe initially I’ve badly explained myself too. If we agree on the basic points,
I think I’ll soon start separate threads to let you know where my experiments are going.

>
> Christoph
>

Greetings,
Nicola






Mail converted by MHonArc 2.6.19+ http://listengine.tuxfamily.org/