Hi,
In examining the viability of converting an existing ocaml codebase to F# I recently ran some performance benchmarks and was pleasantly surprised by the results (reproduced below), The code used to benchmark is not my own - it comes from the ocaml submissions to the
Computer Language Benchmarks Game. I think everybody realises how little you can read into benchmarks. They are afterall particular implementations of particular algorithms (often not written in the dominant idiom of the language), compiled/interpreted with a particular implementation, run on particular hardware etc. So with that said, I hope nobody takes this post as an attempt to 'prove' something definite about the performance of F# code relative to other languages. I've posted this purely because I thought one or two people might be interested.
Some notes on the methodology (in no particular order):
1. Programs were taken from the language shootout ocaml submissions where the ocaml source either needed no changes to compile or the changes were extremely trivial changes in syntax (i.e removal of ocaml named arguments, addition of spaces between brackets).
2. All tests were run three times and the mean taken as the result. The time was measured using the *nix 'time' utility. On windows the tests were run from a cygwin shell in order to have access to this utility.
3. Ocaml programs were built with the native code compiler (ocamlopt) using the flags suggested by the shootout.
4. F# programs were built using VS 2005 release mode on Windows XP. Mono version was 1.2.2.1 with flag --optimize=all. The ahead of time compiler was not used because fsc produces .NET 2.0 assemblies for which aot doesn't work (apparently, I'm no Mono expert).
5. gcc programs were built with the flags suggested by the shootout except with march changed to athlon-xp and -mfpmath=sse -msse2 added in 2 cases where they are omitted by the shootout but produce a performance increase on my machine.
6. Mono F# and gcc were both built and run on Linux (Gentoo). I realise it would have been better to run all benchmarks under one operating system but things didn't work out that way and as stated above - this exercise was always intended to be taken with a pinch of salt.
Some notes on the results:
1. F# performance with the Microsoft runtime is generally very good. In two cases actually beating c code compiled specifically for the testing hardware (see n-body and spectral norm). I'm guessing that this is to do with some very clever floating point arithmetic optimisations in the .NET compiler.
2. F# does poorly on two benchmarks (pidigits and startup). I think its safe to ignore the startup benchmark since most programs actually do some work. I've no idea why F# does badly on pidigits, although one thing I did notice is that ocaml buffers output whereas F# seems to flush the output stream after every call to printf. C blows the competition out of the water on pidigits probably because of the use of gmp.
3. Mono F# is definitely the poorest performer on the benchmark, although most of the times are respectable. This may in part be due to my lack of knowledge about Mono. Personally, I expect Mono performance to increase with future releases.
Results:
| F# .NET | F# Mono (Linux) | ocaml(XP) | ocaml (Linux) | gcc (Linux) |
| Binary Trees (n = 16) | 2.563 | 7.499 | 2.281 | 2.269 | 2.408 |
| Fannkuch (n = 11) | 6.188 | 9.337 | 6.610 | 6.303 | 4.130 |
| Nsieve (n = 9) | 0.906 | 1.805 | 0.844 | 0.712 | 0.539 |
| N-body (n = 20000000) | 7.328 | 36.896 | 36.969 | 23.939 | 7.398 |
| Partialsums (n = 2500000) | 1.610 | 2.395 | 2.422 | 1.648 | 1.314 |
| Pidigits (n = 2500) | 9.515 | 13.428 | 2.016 | 1.647 | 0.370 |
| Recursive (n = 11) | 3.172 | 3.515 | 4.797 | 4.664 | 1.541 |
| Spectral-norm (n = 5500) | 9.843 | 26.253 | 52.859 | 51.222 | 10.889 |
| Startup | 0.235 | 0.062 | 0.031 | 0.001 | 0.001 |
Ben Jackson.