# To what extent is generic and meta-programming using C++ templates useful in computational science?

The C++ language provides generic programming and metaprogramming through templates. These techniques have found their way into many large-scale scientific computing packages (e.g., MPQC, LAMMPS, CGAL, Trilinos). But what have they actually contributed to scientific computing in value that goes beyond non-generic, non-meta languages like C or Fortran in terms of overall development time and usability for equal or adequate efficiency?

Given a scientific computing task, has generic and meta-programming through C++ templates demonstrated an improvement in productivity, expressivity, or usability as measured by any well-understood benchmarks (lines of code, person-effort, etc...)? Correspondingly, what risks are associated with the use of C++ templates for generic and meta- programming?

• I am concerned that this question could be too open to opinions, but I want to see what people in the community say. Mar 23 '12 at 20:06
• I completely deleted the comment derail. If you would like it reposted to chat or meta I am happy to do so. See my meta here. Mar 24 '12 at 9:38
• Also, see the related question here on advice for when to use and when to avoid expression templates. Mar 24 '12 at 14:33
• I do not think that it is correct to say that LAMMPS uses templates or metaprogramming. LAMMPS is object-oriented code that looks a whole lot like Fortran most of the time. I don't think MPQC has much templating either, but it is heavily object-oriented and polymorphic. Oct 5 '12 at 1:43
• OpenFOAM makes heavy use of templates and other features of C++. Sep 17 '16 at 10:05

I think by and large, template metaprogramming has been found to be unusable in practice -- it compiles too slow, and the error messages we get are just impossible to decipher. The barrier to entry for newcomers is also just too high when using metaprogramming.

Of course, generic programming is an entirely different issue, as witnessed by Trilinos, deal.II (my own library), DUNE, and many other libraries -- expressing the same concept operating on different data types is sort of a no-brainer, and the community has largely accepted it as long as it stays within bounds that avoid the problems of metaprogramming. I think generic programming qualifies as an obvious success.

Of course, neither of these topics are immediately connected to OOP. OOP, again, is I would say, universally accepted by the scientific computing community. Even less than generic programming, it isn't a topic of debate: every successful library written in the past 15 years (whether written in C++, C or Fortran) uses OOP techniques.

• tmp may be difficult for novice users, but it is often done very well inside the library. Its one of those techniques that can really reduce the amount of code but you really need to know what you are doing. If you don't believe me go read the source of Eigen or Elemental. Its beautiful code that without templates would pretty much be impossible. Mar 24 '12 at 20:29
• Sure, it's a technique with value. But it's difficult to maintain, and it's often difficult to use if it's exposed to the external interface. That said, I think one of the reasons TMP hasn't quite been the success people might have expected initially is that compilers have just become very good. TMP lives off the fact that a lot of things are known at compile time, and can then be propagated as constants into the actual code. But compilers have become quite good at inlining, function cloning, etc, and so a significant part of the benefit can today be obtained using "normal" programming. Mar 24 '12 at 21:33

Let me give an example based on experience. Most libraries I use from a day to day basis use OOP in some way. OOP is able to hide the complexity required for many domains, it is not a mechanism that really helps with performance. What can happen is that a library is able to use specific optimizations based upon the object hierarchy, but for the most part is about hiding complexity from the user. Look up Design Patterns, they are the mechanisms often employed to accomplish this complexity hiding.

Take PETSc as an example. PETSc uses an inspector/executor model of OOP where any of its algorithms looks at the available routines in a given object an chooses which to execute to accomplish the routine. This allows a user to separate concerns, for example the matrix action can include any sort of blocked or optimized routine and be effectively used by numerous iterative solvers. By giving the user the ability to specify their own data types and evaluations, they get a few important routines sped up and also have the entire library's functionality still available.

Another example I'll give is FEniCS and deal.II. Both of these libraries use OOP to generalize over a large number of Finite Element Methods. In both everything from element type, element order, quadrature representation, and so on is interchangable. While both these libraries are "slower" than some special-purpose structured FEM codes, they are able to solve a wide variety of problems with much of the complexity of FEM unknown to the user.

My final example is Elemental. Elemental is a new dense linear algebra library that has taken the difficulty of managing MPI communicators and data location to a very simple language construct. The result is that if you have a FLAME serial code, by changing the datatypes you can also have an parallel code via Elemental. Even more interesting you can play with the data distribution by setting on distribution equal to another.

OOP should be thought of as a way to manage complexity, not a paradigm for competing with hand rolled assembly. Also doing it poorly will result in a lot of overhead thus one must keep timing and updating the mechanisms they use it with.

What language features like OOP do for scientific computing is make for more compact code statements which helps in understanding and using code better. For example, FFT routines need to carry a large number of arguments for each function call making the code cumbersome.

By using module or class statements only what is needed at the time of the call can be passed, as the rest of the arguments pertain to problem setup (i.e. size of arrays, and coefficients).

In my experience, I had SUBROUTINE calls with 55 arguments (in & out) and I reduced that to 5 making the code better.

That's value.

I am a strong advocate of generic programming and meta-programming for scientific computing. I am actually developing a free software C++ library for Galerkin methods based on these techniques called Feel++ (http://www.feelpp.org) which is steadily getting momentum. True that there are still difficulties such as slow compilation times and that the learning curve could be steep if one wants to understand what is going on behind the scene. This is however extremely interesting and mind blowing. If done at the library level and hiding the complexity behind a domain specific language, you get an extremely powerful tool. We have at our disposal a very wide range a methods to use and compare. For teaching purpose of scientific computing this is awesome, for research and new numerical methods too, for large scale applications, well we work on it but so far so good, we can already do some nice stuff. We have engineers, physicists and mathematicians using it: most of them just use the language for variational formulation and they are happy with it. Looking at some of the formulations our physicists colleagues manipulate, I wouldn't want to see them done "by hand" without a high level language to describe the variational formulation. I personally consider that these "techniques" or "paradigms" are now necessary to tackle complexity in scientific computing code with having to multiply the code size by a huge factor. There is problably a need to improve the support of meta-programming in C++ but it is already in good shape especially since C++11. Compilers support is getting much better and the Boost C++ libraries show the way to proper handling of advanced generic/meta-programming.

You might find the paper http://arxiv.org/abs/1104.1729 relevant to your question. It discusses expression templates (a particular application of template meta-programming used in scientific code) from the perspective of performance.

• That paper drives me insane. Compare the fastest plain Fortran you have to MKL and it will lose as well. Hand tuned assembly is not something one aspires for, it is what you do when every nanosecond counts and can be reused by a very large number of folks. Mar 24 '12 at 20:36
• @aterrel: This is precisely the contrast that I'm wondering about. Knowing that you will have to do hand-optimization as the very last stage of development, which language would you choose as base to use prior to the last stage? Do we have hard data to suggest which language to choose? Mar 26 '12 at 20:42
• @Deathbreath: I'll repeat an answer I've made on several other threads as well -- that by and large, tuning the last bit of speed out of your code is something you do very rarely. But you program high level algorithms all the time. So choose the language that allows you to do the big stuff quickly. There is always a way to include low-level stuff somehow, but it shouldn't be the thing that determines your choice of programming language. Mar 26 '12 at 21:24

Templates are very good at removing type/domain checks at run-time. These can be taken care of at compilation time. This can in theory increase performance over the same type of implementation in C or Fortran where type checking can only be done at run-time - checks are implemented in the source code. However, you can achieve the same results in C using precompiler options but these have to be done by hand unlike templates.

However, templates can produce significant overhead too. They can often create code bloat which can impact upon the use of the instruction cache. Furthermore generic approaches can often shackle the compiler during optimisation - it isn't always straight forward for code analysis when use generic approaches. It is always a problem with automation - including compiler optimisation - very often the output code is not cache friendly.

The benefits of type/domain checking, although certainly safer, is the only real benefit I can see in terms of performance and these are typically imperceptible. But as I say the overall effect can be negative depending what you're doing. That's why it is often better to hand optimise your code where you have significant bottlenecks.