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