caml-list - the Caml user's mailing list
 help / color / mirror / Atom feed
From: Pierre Chopin <pierre@punchup.com>
To: Goswin von Brederlow <goswin-v-b@web.de>
Cc: caml-list@inria.fr
Subject: Re: [Caml-list] exn vs option
Date: Thu, 5 Apr 2012 16:19:08 -0400	[thread overview]
Message-ID: <CAGyUfm0=K7ASggPYXnUhKDHuoH=vdD4avmsruKm9sYufqeX8YQ@mail.gmail.com> (raw)
In-Reply-To: <874nsy1rcd.fsf@frosties.localnet>

[-- Attachment #1: Type: text/plain, Size: 5015 bytes --]

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

> 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
>



-- 
Pierre Chopin,
Chief Technology Officer and co-founder
punchup LLC
pierre@punchup.com

[-- Attachment #2: Type: text/html, Size: 6492 bytes --]

      parent reply	other threads:[~2012-04-05 20:19 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
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 [this message]

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='CAGyUfm0=K7ASggPYXnUhKDHuoH=vdD4avmsruKm9sYufqeX8YQ@mail.gmail.com' \
    --to=pierre@punchup.com \
    --cc=caml-list@inria.fr \
    --cc=goswin-v-b@web.de \
    /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).