Outperforming LAPACK with C++ metaprogramming
wordsandbuttons.online
wordsandbuttons.online
I have worked with these guys and all I can say is good luck outperforming the highly optimized routines they have written. My bet is that the the guy writing this blog used a non optimized version of lapack.
The performance is completely expected--in fact, I would expect an optimized 5x5 solver to be faster than this.
Though if the matrices are guaranteed to be small (like in this example), libxsmm [1] is specialized and highly optimized for this use case and beats its competitors above.
And yes, it's absolutely essential to make sure you didn't just move computations to compile-time.
As a quick and dirty hack that took about half hour to implement, I decided to just have the program as a template string and the parameters be substituted in by a python script that called GCC. To the amazement of a very sceptical friend, the result was a 5x speedup.
A lot of this speedup was consistent with replacing the integer divisions with multiplicative inverses, but also merging constants in a way that would have made things quite unreadable. I was quite impressed with how far GCC had gone.
Considering each instance of this simulation ran for tens of minutes, it was well worth compilation time of a few seconds. It kept the code really simple, too.
edit: oh nm, reread GP...
I think if I had to do something similar again today, any script would output a shell script that used command line macro definitions. That didn't really occur to me at the time.
The simulation was run about 100 times with different parameters. Instead of taking about an hour each they instead took about 12 minutes each, with the only difference being baking in all the parameters as constants and letting GCC do the rest.
If you're going to figure this out, please output a warning and ask me to call the optimized intrinsic or something.
These optimizations really mean that as software writers we can declare our intent and have the compiler do safe optimizations.
It seems like these optimizations of Clang are also quite insensitive to the many different ways of writing the same thing that C++ has earned notoriety for.
Huh? It's not explicit, the purpose is implicit. You are writing down an algorithm that implements a popcount algorithm. The compiler than has to infer the fact that you actually wanted popcount (maybe you just wanted to waste a bit of time in a timing loop?) and then substitute something based on that inference.
I am all for it being explicit, i.e. some library function that has a name that somewhere mentions "popcount" or some such. Then have the compiler generate whatever code it needs for that function.
Without even reading TFA, this was my first thought, remembering back to implementing Fibonnaci's sequence in template metaprogramming from "Thinking in C++, Volume 2": http://web.mit.edu/merolish/ticpp/TicV2.html#_Toc53985733
It's cool and useful to be sure, but as costrouc pointed out, LAPACK has been optimized for a very long time by some very smart people. Most people who have not studied the field for a bit are unlikely to come across a more optimal method.
LAPACK is typically optimized for larger systems, not 5 unknowns. Also, 5 is not a great number for vectorized operations - it might even be beneficial to zero/one pad the matrix.
Optimized LAPACK is often 5-10x faster than «basic LAPACK».
BLAS/LAPACK was originally written in the 70s/80s and sometimes unroll loops to the tune of 5 or 7 - it made sense then. Not so much these days.
I didn’t read the article in detail, but there appear to be a lot of holes in it.
Or just use a template specialisation:
I mean, it's neat trick and all, but the comparison with LAPACK does not really stand on equal ground.
> brute force Cramer's rule solution
This isn't really a fair comparison: trusty old DGESV will yield far more accurate results for badly conditioned problems since it does row swaps (partial pivoting). It might also rescale values; I don't remember the details. It would be much more reasonable to compare a hypothetical DGEC (double precision general matrix cramer's rule) routine.
http://docs.mir.dlang.io/latest/index.html
http://blog.mir.dlang.io/glas/benchmark/openblas/2016/09/23/...
That inflammatory wording doesn’t add anything to your comment. If you wanted to compare D with C++ it’d be a lot more useful to link to something which does that rather than just the documentation index.
C++ has for a long time embraced metaprogramming and given new tools for it (I think there's `static if` now? Or will be soon?), but it really all started out as an accident:
https://softwareengineering.stackexchange.com/questions/1253...
> If you wanted to compare D with C++
My second link does that, compares Mir to a bunch of other numeric libraries, most in C++.
Edit: (Replying to comment below by acdha because HN does not allow me to post new comments right now.)
I don't have that kind of example handy. I can point you to the D Gems section of the D tour to give you a taste of what is possible in D. Click on "Gems" in the navigation menu above to see them.
https://tour.dlang.org/tour/en/gems/uniform-function-call-sy...
If you want a detailed blog post comparing D and C++ metaprogramming, I don't have it ready yet. If you're curious to learn about it, look in more depth at the links I've provided. I'm sorry I don't have the resources to give you the detailed answer you require.
> My second link does that, compares Mir to a bunch of other numeric libraries, most in C++.
This is technically correct but not helpful because it just shows a bunch of benchmark results. If the point is to demonstrate how D is more powerful for this kind of programming what would be useful is something which shows the same function written in modern C++ and D so you could see how they differ.
Yet neither of them are as elegant as LISP macros.
I think D's metaprogramming is very close to lisp, possibly as close as possible without having sexps.
> it's not so bad
You can always use the C preprocessor in C++ if raw string manipulation is enough.
Seriously, though, constexpr means C++ also has static if, compile time loops, function eval, etc.
> constexpr means C++ also has static if, compile time loops, function eval, etc.
It's still not quite the same. I'm sorry to not bring examples, but if you look for them yourself, I think you'll find that D's metaprogramming looks nicer than C++'s constexpr.
Not even close. You can't conditionally insert a member.
(defmacro when (condition &rest body)
`(if ,condition (progn ,@body)))
and then you think hard about where the leaks are. Or else you take the Scheme approach of hygienic macros which are even less elegant. Macros in Lisp are powerful, but I'm not sure metaprogramming in Lisp is better than in a language like D.I don't think you actually understand anything about the example code you posted. There is no special "complicated sublanguage" - the backquote[1] is an extension of the quote shorthand notation[2] for list literals. It is for building data structures and has nothing to do with "sublanguages" or macros. There is no special syntax for macros in Common Lisp. Common Lisp macros do not produce source code - they produce any type of Common Lisp objects directly. That is why they are simpler and strictly more powerful than D templates.
When it comes to templates, I wouldn't call anything from this list of D examples[3] elegant.
[1] http://www.lispworks.com/documentation/HyperSpec/Body/02_df.... [2] http://www.lispworks.com/documentation/HyperSpec/Body/s_quot... [3] https://dlang.org/templates-revisited.html
This doesn't add anything to the conversation. If you want to discuss my post, I'm happy to discuss. You're clearly upset that I didn't praise Common Lisp, but things like that don't lead to useful discussion.
> There is no special "complicated sublanguage" - the backquote[1] is an extension of the quote shorthand notation[2] for list literals. It is for building data structures and has nothing to do with "sublanguages" or macros. There is no special syntax for macros in Common Lisp.
I've never seen anyone write macros using only s-expressions. True, you don't need to learn any special syntax to do it, but that's not how anyone writes macros.
> When it comes to templates, I wouldn't call anything from this list of D examples[3] elegant.
How is that is relevant to my comment?
No, I am upset that you are posting misinformed comments about Common Lisp macros vs D templating while representing yourself as being knowledgeable about the subject. You clearly do not understand how the former works.
> I've never seen anyone write macros using only s-expressions. True, you don't need to learn any special syntax to do it, but that's not how anyone writes macros.
Please stop pretending that you have enough experience with Common Lisp to say things like "I've never seen anyone do X." Writing macros that call functions that return code is a basic technique - Graham's On Lisp is full of examples. Saying that quoting as a way of specifying list literals is a "complicated sublanguage" is on the level of "C++ templates have too many pointy brackets" of criticism.
I kind of fail to see how it is especially complicated. The macro uses a general mechanism to create lists. This mechanism isn't tied to macros and does not do anything special in macros.
It's also not really a sublanguage.
> then you think hard about where the leaks are
There are no leaks in the example.
Even more interesting would be whether this leads to the same huge performance gains in those languages as well.
For numerical applications, one interesting project that comes to mind is the Terra programming language. It uses a flexible high level language (Lua) to metaprogram a fast and predictable C-like language (Terra). The advantage compared to the examples you mentioned is that each language can focus on its strength: the low level language being similar to C allows the programmer to better reason about performance and having a high level language for metaprogramming keep things simple.
There are also systems out there specifically designed for numeric applications that let you specify an algorithm in a high level DSL and generate highly performing C code, some of which might even tesr various versions of the code to find one that runs better on your machine (for example, the best way to iterate the data might be affected by cache sizes). I don't remember the names of these off the top of my head but the Terra PhD thesis has lots of references to them.
I was doing primarily research code at the time though, and the overall improvement in productivity more than made up for the fractional difference in run times for me. Of course, ymmv.
In some domains (e.g. linear algebra) this can be very powerful because you don't have write non idiomatic expressions just to feed the compiler things it can understand.
Here's how I modified the code to make sure: https://pastebin.com/iG21pTiP
http://stsievert.com/blog/2015/01/31/the-mysterious-eigenval...
At least, that's how I interpreted the comment...
More importantly though, the code would probably take ages (as in, "age of the universe" ages) to compile, as Cramer's rule is O(n!) (using a naive implementation, I think one could get to O(n^4) if determinant was computed using LU factorization, but what's the point of Cramer's rule then...).