F# Has Better Performance than C# in Math
As it turns out algorithms coded in F# code might have much better performance, compared to the ones in C# code.
Below you will find F# version of optimized sigma-comparison algorithm from the previous post.
Compared to the C#, this .NET code:
- executes sigmoid1 benchmark in 588.8ms instead of 3899,2ms
- executes sigmoid2 (LUT) benchmark in 156.6ms instead of 411.4ms
You'll see below that this performance of F# is quite close to the performance of the similar implementation written in C.
Here's the F# code:
#light
let Scale = 320.0f;
let Resolution = 2047;
let Min = -single(Resolution)/Scale;
let Max = single(Resolution)/Scale;
let range step a b =
let count = int((b-a)/step);
seq { for i in 0 .. count -> single(i)*step + a };
let lut = [|
for x in 0 .. Resolution ->
single(1.0/(1.0 + exp(-double(x)/double(Scale))))
|]
let sigmoid1 value = 1.0f/(1.0f + exp(-value));
let sigmoid2 v =
if (v <= Min) then 0.0f;
elif (v>= Max) then 1.0f;
else
let f = v * Scale;
if (v>0.0f) then lut.[int (f + 0.5f)]
else 1.0f - lut.[int(0.5f - f)];
let getError f =
let test = range 0.00001f -10.0f 10.0f;
let errors = seq {
for v in test ->
abs(sigmoid1(single(v)) - f(single(v)))
}
Seq.max errors;
open System.Diagnostics;
let test f =
let sw = Stopwatch.StartNew();
let mutable m = 0.0f;
let result =
for t in 1 .. 10 do
for x in 1 .. 1000000 do
m <- f(single(x)/100000.0f-5.0f);
sw.Elapsed.TotalMilliseconds;
printf "Max deviation is %f\n" (getError sigmoid2)
printf "10^7 iterations using sigmoid1: %f ms\n" (test sigmoid1)
printf "10^7 iterations using sigmoid2: %f ms\n" (test sigmoid2)
let c = System.Console.ReadKey(true);
And the output (Release compilation against F# 1.9.6.2 CTP with no debugger):
Max deviation is 0.001664
10^7 iterations using sigmoid1: 588.843700 ms
10^7 iterations using sigmoid2: 156.626700 ms
Chances are that the performance of sigmoid2 is better than that, but I just can't figure out how to get rid of the mutable state.
Running as Mono
By the way, that's how similar (although not exact) code works out under Mono (details):
$ gmcs test.cs && mono test.exe
Max detected error using LUT: 0.1568425 %
1000000 iterations using Sigmoid1() took 3591.858 ms
1000000 iterations using Sigmoid2() took 247.561 ms
Running as C
C is supposed to be really close to metal and highly efficient. Let's check out how it works out for this algorithm.
Running C implementation of the code (developed by Henrik Gustafsson) yields following results:
Max deviation is 0.001664
10^7 iterations using sigmoid1: 628 ms
10^7 iterations using sigmoid2: 157 ms
Surprisingly enough, the performance is almost the same as F#. So F# must be doing really good job here.
Caveat: I've been using win32 compiler to get the binaries. 64 bit might improve the performance a bit. I'll update the article if it does.
Summary
So it turns out that F# compiler creates code that is 2-6 times faster for the math code, compared to C# (while the output is still .NET compatible). And this F# code has performance close to the one of the C code.
All this makes me wonder, if it is worth switching to F# for the core NeuroEvolution code. Stay tuned for the series.
Tuesday, January 6, 2009 at 15:35