caml-list - the Caml user's mailing list
 help / color / mirror / Atom feed
From: Goswin von Brederlow <goswin-v-b@web.de>
To: Pierre Chopin <pierre@punchup.com>
Cc: caml-list@inria.fr
Subject: Re: [Caml-list] exn vs option
Date: Thu, 05 Apr 2012 11:05:22 +0200	[thread overview]
Message-ID: <874nsy1rcd.fsf@frosties.localnet> (raw)
In-Reply-To: <CAGyUfm0SkjTVZ4hcQRNGThWWzGoXbbVnBXHBDcr1jP=6cFCMkQ@mail.gmail.com> (Pierre Chopin's message of "Wed, 4 Apr 2012 16:25:48 -0400")

Pierre Chopin <pierre@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

  parent reply	other threads:[~2012-04-05  9:11 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-04-04 20:25 Pierre Chopin
2012-04-04 20:38 ` John Carr
2012-04-04 22:10   ` Julien Verlaguet
2012-04-05  1:29     ` Francois Berenger
2012-04-05  6:45 ` Raphael Proust
2012-04-05  7:53   ` Benedikt Grundmann
2012-04-05  9:05 ` Goswin von Brederlow [this message]
2012-04-05  9:50   ` Daniel Bünzli
2012-04-11 10:26     ` Goswin von Brederlow
2012-04-11 10:32       ` David House
2012-04-11 10:36         ` David House
2012-04-05 20:19   ` Pierre Chopin

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=874nsy1rcd.fsf@frosties.localnet \
    --to=goswin-v-b@web.de \
    --cc=caml-list@inria.fr \
    --cc=pierre@punchup.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).