Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Hey, since this is likely to attract Haskell programmers: how fast is Haskell these days for a programmer intent on writing optimized code? I am particularly interested in its performance for numerical crunching like matrix operations and other stuff that benefit from SIMD.


Haskell's speed can be competitive with systems languages but keep in mind that its killer feature is ease of abstraction.

The idea is that it is simple to assemble multiple parts into a coherent, well organised program. Which is important for the entirety of the program, no just the tight loop.

So, with the nice FFI Haskell has, you can always drop down to languages without a GC for inherently imperative optimisations. Then you wrap that into a library with nice types and you can now leverage that raw power anywhere in your Haskell code where the types will match.

I worked at Meta in a high performance Haskell application and that's what we did. Wrote beautiful, large, fast Haskell programs which in some specialised parts had C++ building blocks. 99% of the time was spent on Haskell land composing things into more and more useful applications.


I like Haskell performance for every-day backend/web and CLI stuff. But I drop down into Rust when I'm writing something performance-focused.

That said, Haskell's no slouch. Here's a small program to count the 1-bits in a file.

  main :: IO ()
  main = do
    content <- getArgs >>= \[a] -> unsafeMMapVector a Nothing
    print (vectorPopCount content)

  vectorPopCount :: V.Vector Word64 -> Int
  vectorPopCount = V.foldl' (+) 0 . V.map popCount
When you compile with -msse4.2, it will correctly use the hardware popcount instruction, and crunches through a 1GB input file in 0m0,090s. Rounding to the nearest MB, it uses 0 heap. (For the curious, if I compile without -msse4.2 it runs in 0m0,293s).

I haven't tried crunching matrices, but I would start by checking out repa, accelerate, or massiv.

  https://hackage.haskell.org/package/repa
  https://hackage.haskell.org/package/accelerate
  https://hackage.haskell.org/package/massiv


The lack of heap allocations is great! Thanks for the pointers.


I met Sam Derbyshire at ZuriHac who told me all the difficult architectural work had been done for SIMD support.

+ https://gitlab.haskell.org/ghc/ghc/-/issues/7741

It might make it for GHC 9.12 (for 128 bit vectors only, and mostly floating-point operations unless other people come in and contribute).

The patch is at:

+ https://gitlab.haskell.org/ghc/ghc/-/merge_requests/12860


Thanks for the info!


The reality is that for any language, including C, compiler optimized code will never be as fast as hand optimized code in libraries like BLAS. So at some level, the choice of host language doesn't matter very much, because you're going to be outsourcing all of the computation anyway if you're really serious about speed. This is the same reason all the AI stuff, possibly the single largest consumer of compute in the world, gets away with being written in python except for the low level compute libraries.

To answer your question directly, The GHC compiler is very good. High level code will perform very well, and for most realistic applications, performance bottlenecks are architectural, not e.g. the use of single width versus SIMD, and the "architectural asymptotics" of haskell are very favorable. I think GHC has/is getting SIMD support but that's not what I would focus on when evaluating perf.

I wouldn't write a matrix multiplication algorithm in Haskell, but I also wouldn't write one in rust or C if I was serious about speed.

Many focus on number crunching as a performance metric, but almost no one is ever actually bottlenecked on that, and if they are it doesn't really matter what high level language they're using.


> The reality is that for any language, including C, compiler optimized code will never be as fast as hand optimized code

That's not strictly true; sometimes a C compiler can optimize away my whole program: https://godbolt.org/z/oG5nfGE6z


You're doing extremely simple constant arithmetic here. GHC can optimize this type of thing away to nothing as well. Are we talking about contrived examples or real numerical bottlenecks?


I think it was a joke about compilers removing code by reasoning about undefined behavior.


Haskell really shines when you want to write high level, declarative code. Performance when using this style is generally fine for CLI / web backend style stuff. It has the tools to write pretty fast low level code, but they’re fairly clunky and if that’s all you want to write it’s probably not going to be the best tool for the job. They’re pretty nice if you have a few focused hotspots you need to optimize.

It has pretty nice CPU profiling tools, so finding and optimizing CPU hotspots is fairly pleasant. Tracking down rouge memory leaks (which lazy evaluation makes more likely) on the other hand can be extremely frustrating.

If you look at the benchmarks game results [1], the fastest haskell implementations are generally between 2 and 5 times slower than the fastest c versions, and will be written in a highly imperative style.

[1]: https://benchmarksgame-team.pages.debian.net/benchmarksgame/...


It's doable but harder than in imperative languages and the resulting code is _really_ ugly. See the thread of the 1BRC https://discourse.haskell.org/t/one-billion-row-challenge-in... with example code at (I hope that's the right Gist) https://gist.github.com/AndrasKovacs/e156ae66b8c28b1b84abe6b...


If you want to write fast stuff that takes advantage of SIMD, you want to use ISPC, which is made for high performance SIMD. Using haskell for this would be like doing surgery with a dull rock, you will never get what you want.


I’m not the best person to answer this question, but AFAIK it’s very very fast (in the rough vicinity of C). But also memory-hungry.


I'm pretty sure highly optimised code won't be elegant Haskell code though.


Highly optimized code tends to be inelegant in any language. That said, you can get really good performance from very elegant looking “normal” Haskell too. The big challenge with performance in Haskell is that the differences between optimized and unoptimized code can be pretty subtle if you’re not used to thinking about Haskell performance.


Let me clarify: naive/beautiful/effortless Haskell code tends to be highly performant, although that is not always true. I believe it is much easier to write fast Haskell code than to write fast C/C++ code.


If you’re writing idiomatic Haskell. Its performance is terrible.

If you’re choosing to fight with Haskell, why? Just use something else.

To understand why people claim Haskell is “fast”, you need to understand what they mean. What they mean is “if you opted to write C in such a way as you were performing similar amounts of copious and useless data copying, pointer following and stack blowing madness, Haskell will perform that fast”. They are not saying “Haskell is as fast as the fastest idiomatic C implementation”.

Another thing you’re going to see a lot of is extremely simple anecdotes, such as counting in a loop, or favourable measure points (they will measure the whole c program, but just the point in Haskell after they’ve flattened data, for example, stating “we just want to compare those parts”).


I think if your runtime performance is terrible with Haskell, either the explanation is that you might be doing something a bit crazy, or that your application is memory-constrained.


Uh no.

The explanation is that Haskell has terrible performance idioms like “copying shit for no reason all the time”.

There’s a reason that the prevailing opinion on language performance within the Haskell community is “thinking about performance of a language is a premature optimization”

This, of course, ignores that Haskells poor performance characteristics are actually technical debt, for which all people should be considering off the bat for their project. You cannot simultaneously say “premature” and not also add this to the techdebt column.

There comes a time in *all* scaling applications that Haskell will be such a burden, that it’ll be forced to be rewritten.


It seems we’re in complete polar disagreement. None of the observations you make match mine.


Yeah. And one of us is objectively, measurably correct, while the other is speaking in terms of “performance is a premature optimization” Haskell community brainwashing tomfoolery.

I mean. Fundamentally, reducing cache invalidation, reducing pointer following, and branch prediction are like 80% of your performance gains today. Haskell, being bad at all of these, fundamentally will never perform from a language standpoint.

You can make all the “I believe!!!” Arguments you like. Belief is not fact. Fact is that Haskell measurably performs badly, and Haskell idioms will never perform well.

If your organization is okay with accepting that huge performance tech debt, that’s a choice for your org.


> one of us is objectively, measurably correct

In concrete terms, what are these Haskell idioms, what are their measured performance characteristics, and what are alternative idioms for each that perform better? Is there a write up that you could share about this?

I think it would be truly educational. Without that though, your statements appear equally as anecdotal.


> the other is speaking in terms of “performance is a premature optimization”

While I do think this is often and perhaps even usually true, it’s irrelevant to anything I’ve said in this thread, and I wasn’t even thinking in these terms.

You’re hearing things.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: