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 <goswin-...@web.de>wrote:

> Pierre Chopin <pie...@punchup.com> 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
pie...@punchup.com

-- 
Caml-list mailing list.  Subscription management and archives:
https://sympa-roc.inria.fr/wws/info/caml-list
Beginner's list: http://groups.yahoo.com/group/ocaml_beginners
Bug reports: http://caml.inria.fr/bin/caml-bugs

Reply via email to