From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail1-relais-roc.national.inria.fr (mail1-relais-roc.national.inria.fr [192.134.164.82]) by walapai.inria.fr (8.13.6/8.13.6) with ESMTP id q35KJFLr031789 for ; Thu, 5 Apr 2012 22:19:15 +0200 X-IronPort-Anti-Spam-Filtered: true X-IronPort-Anti-Spam-Result: AqoBAPX8fU/RVdc2imdsb2JhbABFr3EBiHkIIgEBAQoJDQcSBiOCCQEBAQMBEgITUgULCws7IhIBBQEcBhMih14DBgWcHQqUQyKJPpBaBJVrgRGNQz2EJg X-IronPort-AV: E=Sophos;i="4.75,378,1330902000"; d="scan'208";a="152911875" Received: from mail-lpp01m010-f54.google.com ([209.85.215.54]) by mail1-smtp-roc.national.inria.fr with ESMTP/TLS/RC4-SHA; 05 Apr 2012 22:19:09 +0200 Received: by lagv3 with SMTP id v3so3486274lag.27 for ; Thu, 05 Apr 2012 13:19:08 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=mime-version:in-reply-to:references:date:message-id:subject:from:to :cc:content-type:x-gm-message-state; bh=9bmWOKiCb0IppICfEaqMUIXM3fwyILWFwt6ygntTAW0=; b=c399mwph6rRfEZUY5Qi7zbgbxEG8+RF7LEDI8RMTseMRANOddEB84dMwQ2xWybaGoT d119hkHXusS6r/+HoNMfs3APr5yvyODVoS9vcrZ87483KX0xHH171oOZoGuXKVvqTjlB Q5fndyoaiLrX1N1qtXfm4R2W6kP3OR8D+6jNSueebf23FFV2/wXjAyNHbayFbPfSSGtI S3Hrwd3DqqSqN//jXVH56btVVpuDgA0IaW9YW7yMUL7MDpCuGnprm/wAs1D+0je+yR+r jywk3D4Nent+UWroJGixLjO6j+/QPF6Bb1XhMy7hlVAE6qD7Leww0iNEpDXVo2gqkEHk E6hA== MIME-Version: 1.0 Received: by 10.152.131.9 with SMTP id oi9mr5063145lab.6.1333657148719; Thu, 05 Apr 2012 13:19:08 -0700 (PDT) Received: by 10.112.55.2 with HTTP; Thu, 5 Apr 2012 13:19:08 -0700 (PDT) In-Reply-To: <874nsy1rcd.fsf@frosties.localnet> References: <874nsy1rcd.fsf@frosties.localnet> Date: Thu, 5 Apr 2012 16:19:08 -0400 Message-ID: From: Pierre Chopin To: Goswin von Brederlow Cc: caml-list@inria.fr Content-Type: multipart/alternative; boundary=f46d042c64779a018504bcf440a5 X-Gm-Message-State: ALoCoQmXjftxYFxWVigzVCADJ6Cm0pn6M+tLNHb94N//cjwPtSoRlwwB12l5Y7YOC4QVrfbEspDh Subject: Re: [Caml-list] exn vs option --f46d042c64779a018504bcf440a5 Content-Type: text/plain; charset=ISO-8859-1 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 --f46d042c64779a018504bcf440a5 Content-Type: text/html; charset=ISO-8859-1 Content-Transfer-Encoding: quoted-printable Thank you for your answers.
I am actually in the case of a webserver wh= ere I need to avoid interrupting it, even in 'exceptional' cases, b= ut 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, A= pr 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 exc= eption
> that is caught, in the other the function returns an option that is pa= ttern
> matched on.
>
> I noticed that, whether the exception is thrown or not, the option ver= sion is
> always faster.
>
> Is there any case where it makes sense, performance wise, to use excep= tion
> instead of 'a option ?

I find that in most cases speed is not a major concern and then it co= mes
down to taste and readability of the code.

> test1.ml
> ----------------------------------------------------------------------=
>
> exception Foo
> let f x =3D
> =A0if x =3D1 then raise Foo else ()
>
> ;;
>
> =A0for i =3D 0 to 10_000_000 do
> try
> =A0 =A0 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 =3D
> =A0 =A0 if x=3D1 then None else Some ()
>
> ;;
> for i =3D 0 to 10_000_000 do
> =A0 =A0 match f 1 with
> =A0 =A0 =A0 =A0 None -> ()
> =A0 =A0 | =A0 Some s -> s
> =A0 =A0 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 =3D if x =3D1 then raise Foo else ()

let () =3D
=A0try
=A0 =A0for i =3D 0 to 1000000000 do
=A0 =A0 =A0f 0
=A0 =A0done
=A0with 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 =3D function
=A0| 1 -> raise (Result acc)
=A0| n -> fac (n * acc) (n - 1)

let () =3D
=A0for i =3D 0 to 100_000_000 do
=A0 =A0try
=A0 =A0 =A0fac 1 50
=A0 =A0with Result _ -> ()
=A0done

71.88s user 0.00s system 99% cpu 1:11.90 total


let rec fac acc =3D function
=A0| 1 -> acc
=A0| n -> fac (n * acc) (n - 1)

let () =3D
=A0for i =3D 0 to 100_000_000 do
=A0 =A0ignore (fac 1 50)
=A0done

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 =3D function
=A0| 1 -> raise Error
=A0| n -> n * (foo (n - 1))

let () =3D
=A0for i =3D 0 to 100_000_000 do
=A0 =A0try
=A0 =A0 =A0ignore (foo 50)
=A0 =A0with Error -> ()
=A0done

25.03s user 0.01s system 99% cpu 25.068 total

let rec foo =3D function
=A0| 1 -> None
=A0| n -> match foo (n - 1) with None -> None | Some x -> Some (n= * x)

let () =3D
=A0for i =3D 0 to 100_000_000 do
=A0 =A0ignore (foo 50)
=A0done

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 a= nd 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 th= e
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 =3D ....

let raise_on_none exn f arg =3D
=A0match f arg with
=A0| None -> raise exn
=A0| Some x -> x

let find_exn x y =3D 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

=A0 type ('a, 'b) result =3D Result of 'a | Error of 'b

Putting the 2 flavours into different submodules can keep things tidy too.<= br>
MfG
=A0 =A0 =A0 =A0Goswin



-- Pierre Chopin,
Chief Technology Officer and co-founder
punc= hup LLC

--f46d042c64779a018504bcf440a5--