For this task F# is 2.24 times as fast.
OCaml -----------------------
let runs = 100
let max_iterations = 1000
let iterate ci cr =
let bailout = 4.0 in
let rec loop zi zr i =
if i > max_iterations then
0
else
let temp = zr *. zi and
zr2 = zr *. zr and
zi2 = zi *. zi in
if zi2 +. zr2 > bailout then
i
else
loop (temp +. temp +. ci) (zr2 -. zi2 +. cr) (i + 1)
in
loop 0.0 0.0 1
let mandelbrot n =
for y = -39 to 38 do
if 1 = n then print_endline "";
for x = -39 to 38 do
let i = iterate
(float x /. 40.0) (float y /. 40.0 -. 0.5) in
if 1 = n then
print_string ( if 0 = i then "*" else " " );
done
done;;
let start_time = Sys.time () in
for iter = 1 to runs do
mandelbrot iter
done;
print_endline "";
print_float ( Sys.time () -. start_time );
print_endline "";
F# ----------------------------
open System
let runs = 100
let max_iterations = 1000
let iterate ci cr =
let bailout = 4.0 in
let rec loop zi zr i =
if i > max_iterations then
0
else
let temp = zr * zi and
zr2 = zr * zr and
zi2 = zi * zi in
if zi2 + zr2 > bailout then
i
else
loop (temp + temp + ci) (zr2 - zi2 + cr) (i + 1)
in
loop 0.0 0.0 1
let mandelbrot n =
for y = -39 to 38 do
if 1 = n then Console.WriteLine "";
for x = -39 to 38 do
let i = iterate
(float x / 40.0) (float y / 40.0 - 0.5) in
if 1 = n then
Console.Write ( if 0 = i then "*" else " " );
done
done;;
let start_time = Sys.time () in
for i = 1 to runs do
mandelbrot i
done;
Console.WriteLine "";
Console.WriteLine( Sys.time () - start_time );
> On Feb 12, 8:41 pm, w_a_x_...@yahoo.com wrote:
> > OCaml: 5.921 seconds
> > F#: 2.641 seconds
> Wow
> What is your system's specifications ? (OS, compiler version, RAM,
> CPU, ...)
> On my system it took 73 seconds to run it on OCaml
> (I saved your code in an .ML file and opened it with OCamlwin.exe)
I compiled it with ocamlopt.
windows, Objective Caml version 3.11.0 (mingw), 3.2GHz Pentium
> My system: Vista SP1 , 3 GB RAM , 1.8 GHZ (2 cores) and I'm using
> OCaml 3.10.2 for Windows
> The F# code is not correct (Sys.time does not exist in .NET, you
> should use DateTime and TimeSpan. (I'm using FSharp 1.9.6.2, the CTP)
Correct or not, it works. I guess Sys.time is provided for OCaml
compatibility. How do you use TimeSpan?
Did you run the F# program?
> How do you use TimeSpan?
let t1 = System.DateTime.Now
// Do some work.
let t2 = System.DateTime.Now
System.Console.WriteLine( (t2 - t1).TotalSeconds );
You are running an interpreter. You need to compile the OCaml to get decent
performance, of course.
> My system: Vista SP1 , 3 GB RAM , 1.8 GHZ (2 cores) and I'm using
> OCaml 3.10.2 for Windows
> The F# code is not correct (Sys.time does not exist in .NET,
No. Sys.time is provided by the F# Powerpack DLL.
> you should use DateTime and TimeSpan. (I'm using FSharp 1.9.6.2, the CTP)
No. OCaml's Sys.time measures elapsed CPU time whereas DateTime measures
wall clock time.
--
Dr Jon D Harrop, Flying Frog Consultancy Ltd.
http://www.ffconsultancy.com/?u
That is quite common for numerical tasks because OCaml's lack of JIT
compilation forces it to use a uniform representation of values in general.
> OCaml -----------------------
>
> let runs = 100
> let max_iterations = 1000
>
> let iterate ci cr =
> let bailout = 4.0 in
> let rec loop zi zr i =
> if i > max_iterations then
> 0
> else
> let temp = zr *. zi and
> zr2 = zr *. zr and
> zi2 = zi *. zi in
> if zi2 +. zr2 > bailout then
> i
> else
> loop (temp +. temp +. ci) (zr2 -. zi2 +. cr) (i + 1)
> in
> loop 0.0 0.0 1
In this case, your use of a recursive function rather than a loop is
probably leading OCaml to needlessly box and immediately unbox the function
arguments. Using a loop instead should improve performance significantly.
On the other hand, what you are really doing is complex-valued arithmetic
and, consequently, it would be interesting to compare the performance of
idiomatic code that uses the complex number representations provided with
OCaml and F#. You will find that F# runs circles around OCaml there (e.g.
FFT over complexes is 5.5x faster in F# than OCaml) because OCaml cannot
support structs efficiently because it lacks JIT compilation (or whole
program optimization like MLton) so it is forced to box all complex
numbers, which is hugely inefficient in numerical routines.
Furthermore, algorithms like this one can benefit enormously from
parallelism on multicore machines. F# makes that easy whereas OCaml makes
it hard or impossible.
> No. OCaml's Sys.time measures elapsed CPU time whereas DateTime measures
> wall clock time.
>
Thank you
There is no real "best practice" to speak of because F# is a (fast!) moving
target. For example, they have deprecated epsilon_float but provided no
alternative. So I'd say go with whatever works today and that is exactly
what you did. If they do ever remove things like Sys.time then you can
still cut and paste their old code just as I do with epsilon_float. You
haven't really lost out...
Use System.Diagnostics.Stopwatch.StartNew() to measure elapsed real time
on .NET.
> System.Console.WriteLine( (t2 - t1).TotalSeconds );
Use F#'s printf instead of System.Console.Writeline:
printfn "Took %dms" t.ElapsedMilliseconds
> On the other hand, what you are really doing is complex-valued
> arithmetic and, consequently, it would be interesting to compare the
> performance of idiomatic code that uses the complex number
> representations provided with OCaml and F#. You will find that F#
> runs circles around OCaml there (e.g. FFT over complexes is 5.5x
> faster in F# than OCaml) because OCaml cannot support structs
> efficiently because it lacks JIT compilation (or whole program
> optimization like MLton) so it is forced to box all complex numbers,
> which is hugely inefficient in numerical routines.
F# times on my 2 GHz laptop:
2.0629664 using ordinary floats
18.206179 using Math.Complex
The F# code:
open Math.Complex
let runs = 100
let max_iterations = 1000
let iterate cmp =
let rec loop (z:Math.complex) i =
if i > max_iterations then
0
else
// Using magnitude is too slow.
// if magnitude z >= 2.0 then
if (z.r * z.r + z.i * z.i) >= 4.0 then
i
else
loop (z * z + cmp) (i + 1)
in
loop zero 1
let mandelbrot n =
for y = -39 to 38 do
if 1 = n then printfn "";
for x = -39 to 38 do
let i = iterate
(complex ((float y / 40.0) - 0.5) (float x / 40.0)) in
if 1 = n then printf "%s" ( if 0 = i then "*" else " " )
done
done;;
let start_time = Sys.time () in
for i = 1 to runs do
mandelbrot i
done;
printfn "\n%f" (Sys.time () - start_time)
I suspect you are using an old CLR. The latest one includes struct
optimizations that should halve the latter time.
> The F# code:
>
> open Math.Complex
>
> let runs = 100
> let max_iterations = 1000
>
> let iterate cmp =
> let rec loop (z:Math.complex) i =
> if i > max_iterations then
> 0
> else
> // Using magnitude is too slow.
> // if magnitude z >= 2.0 then
> if (z.r * z.r + z.i * z.i) >= 4.0 then
> i
> else
> loop (z * z + cmp) (i + 1)
Note that this uses general complex product rather than the specialized
complex square from the float-based alternative.
> in
> loop zero 1
>
> let mandelbrot n =
> for y = -39 to 38 do
> if 1 = n then printfn "";
> for x = -39 to 38 do
> let i = iterate
> (complex ((float y / 40.0) - 0.5) (float x / 40.0)) in
> if 1 = n then printf "%s" ( if 0 = i then "*" else " " )
> done
> done;;
>
> let start_time = Sys.time () in
> for i = 1 to runs do
> mandelbrot i
> done;
> printfn "\n%f" (Sys.time () - start_time)
Use System.Diagnostics.Stopwatch in F# and Unix.gettimeofday in OCaml to
measure real time.
I get:
2.1GHz Opteron 2352 in 32-bit Debian
OCaml float: 7.57s
OCaml complex: 7.63s
F# Mono 2.2 float: 4.49s
F# Mono 2.2 complex: 15.26s
2.1GHz Opteron 2352 in 64-bit Debian
OCaml float: 3.39s
OCaml complex: 7.12s
F# Mono 2.2 float: 4.40s
F# Mono 2.2 complex: 5.06s
2.2GHz Athlon64 X2 in 32-bit Win XP
F# float: 1.81s
F# complex: 4.77s
The performance of the machines is similar so F# is significantly faster
than OCaml. If you use an algorithm that benefits from a data structure
with unboxed floats (like the FFT) then that speed gap widens as F# becomes
much faster still. OCaml does a lot better on x64 with floats (as usual)
but is still slower.
For some reason, Microsoft's CLR retains the memory layout of structs when
they are used as local variables and, consequently, structs are several
times slower than necessary. Interestingly, LLVM 2.4 and beyond implements
first-class structs as aggregate values that can have complete freedom in
storage format when they are local variables. For example, they may be
stored entirely in registers and not via the stack, and they can be passed
to other functions in registers whereas the CLR passes them by pointer to
the current stack frame (which breaks tail calls that pass structs on the
CLR).
>
> Use System.Diagnostics.Stopwatch in F# and Unix.gettimeofday in OCaml
> to measure real time.
Why isn't Sys.time accurate?
let timer = System.Diagnostics.Stopwatch.StartNew () in
let start_time = Sys.time () in
for i = 1 to runs do
mandelbrot i
done;
printfn "\n%f" (Sys.time () - start_time);
printfn "%O" timer.Elapsed
--- output ---
// floats
2.01289440000002
00:00:02.1956882
// complex
18.196165
00:00:18.8280599
It measures elapsed CPU time and, consequently, can fail to convey speedups
due to parallelism between the mutator and GC when they run concurrently.
> let timer = System.Diagnostics.Stopwatch.StartNew () in
> let start_time = Sys.time () in
> for i = 1 to runs do
> mandelbrot i
> done;
> printfn "\n%f" (Sys.time () - start_time);
> printfn "%O" timer.Elapsed
>
> --- output ---
>
> // floats
> 2.01289440000002
> 00:00:02.1956882
>
> // complex
> 18.196165
> 00:00:18.8280599
What version of the CLR are you running?
> >
> > // floats
> > 2.01289440000002
> > 00:00:02.1956882
> >
> > // complex
> > 18.196165
> > 00:00:18.8280599
>
> What version of the CLR are you running?
I don't know what CLR is.
MSR F# Interactive, (c) Microsoft Corporation, All Rights Reserved
F# Version 1.9.4.19, compiling for .NET Framework Version v2.0.50727
Ok, that's the latest CLR but an old version of F#. I get 2x better
performance in the latter case using the latest F# (1.9.6.2).
"CLR" is Microsoft's Common Language Runtime. It's another name for
the .NET framework.
George
where's that much expected OCaml vs Ruby, William?
> where's that much expected OCaml vs Ruby, William?
\|||/
(o o)
,----ooO--(_)-------.
| Please |
| don't feed the |
| TROLLs ! |
'--------------Ooo--'
|__|__|
|| ||
ooO Ooo
LOL, did Reiner Joswig lend you that? ;)