Thank you for your answers. I am actually in the case of a webserver where I need to avoid interrupting it, even in 'exceptional' cases, but rather report the problem in a log, and return a default value whenever it is possible. Options seem to be the best choice, not only from a safety point of view but also performance-wise. Cheers On Thu, Apr 5, 2012 at 5:05 AM, Goswin von Brederlow wrote: > Pierre Chopin writes: > > > Hi, > > > > I benchmarked two programs, in one case the main function throw an > exception > > that is caught, in the other the function returns an option that is > pattern > > matched on. > > > > I noticed that, whether the exception is thrown or not, the option > version is > > always faster. > > > > Is there any case where it makes sense, performance wise, to use > exception > > instead of 'a option ? > > I find that in most cases speed is not a major concern and then it comes > down to taste and readability of the code. > > > test1.ml > > ---------------------------------------------------------------------- > > > > exception Foo > > let f x = > > if x =1 then raise Foo else () > > > > ;; > > > > for i = 0 to 10_000_000 do > > try > > f 1 > > with Foo -> () > > done > > 0.34s user 0.01s system 99% cpu 0.351 total > > That is rather short for a test. lets add 2 zeroes to the loop. And lets > call f 0 and f 1 to test both cases: > > f 0: 17.72s user 0.02s system 99% cpu 17.792 total > f 1: 35.30s user 0.02s system 99% cpu 35.371 total > > > ------------------------------------------------------------------------ > > test2.ml: > > ------------------------------------------------------------------------ > > let f x = > > if x=1 then None else Some () > > > > ;; > > for i = 0 to 10_000_000 do > > match f 1 with > > None -> () > > | Some s -> s > > done > > ------------------------------------------------------------------------ > > f 0: 11.60s user 0.02s system 99% cpu 11.655 total > f 1: 10.91s user 0.01s system 99% cpu 10.933 total > > And lets test the speed when the exception is actualy exceptional: > > exception Foo > let f x = if x =1 then raise Foo else () > > let () = > try > for i = 0 to 1000000000 do > f 0 > done > with Foo -> () > > 9.94s user 0.00s system 99% cpu 9.946 total > > Someone said in deep recursions exceptions are faster because they don't > have to unwind the stack: > > exception Result of int > > let rec fac acc = function > | 1 -> raise (Result acc) > | n -> fac (n * acc) (n - 1) > > let () = > for i = 0 to 100_000_000 do > try > fac 1 50 > with Result _ -> () > done > > 71.88s user 0.00s system 99% cpu 1:11.90 total > > > let rec fac acc = function > | 1 -> acc > | n -> fac (n * acc) (n - 1) > > let () = > for i = 0 to 100_000_000 do > ignore (fac 1 50) > done > > 67.04s user 0.02s system 99% cpu 1:07.08 total > > > Not feeling it. Lets try something not tail recursive: > > exception Error > > let rec foo = function > | 1 -> raise Error > | n -> n * (foo (n - 1)) > > let () = > for i = 0 to 100_000_000 do > try > ignore (foo 50) > with Error -> () > done > > 25.03s user 0.01s system 99% cpu 25.068 total > > let rec foo = function > | 1 -> None > | n -> match foo (n - 1) with None -> None | Some x -> Some (n * x) > > let () = > for i = 0 to 100_000_000 do > ignore (foo 50) > done > > 49.48s user 0.01s system 99% cpu 49.508 total > > > In conclusion I would have to say that exceptions are better if they are > exceptional. > > When you do not catch them right away or not at all then they are > better. The "try" command is more expensive than an option type and if > you are going to catch the exception right away anyway then options are > faster. > > But if you don't catch them right away the cost of the try can be > amortized over many calls and becomes cheaper. Or if you don't catch the > exception at all then you can get a nice backtrace of where the > exception occured. > > If your code is not tail recursive then option types mean you have to > match them on every level again and again and allocate a ton of 'Some x' > if no error occurs. You can make your code tail recursive or use > exception to improve performance there. > > > > If you are writing a module then consider providing both flavours for > functions, one with exceptions and one with options. Even if you only do > something like this: > > let find x y = .... > > let raise_on_none exn f arg = > match f arg with > | None -> raise exn > | Some x -> x > > let find_exn x y = raise_on_none Not_found (find x) y > > Obviously option only works for exceptions like Not_found. If you want > to return an error you might have to use something like > > type ('a, 'b) result = Result of 'a | Error of 'b > > Putting the 2 flavours into different submodules can keep things tidy too. > > MfG > Goswin > -- Pierre Chopin, Chief Technology Officer and co-founder punchup LLC pierre@punchup.com