From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail4-relais-sop.national.inria.fr (mail4-relais-sop.national.inria.fr [192.134.164.105]) by walapai.inria.fr (8.13.6/8.13.6) with ESMTP id q359BAbt012325 for ; Thu, 5 Apr 2012 11:11:10 +0200 X-IronPort-Anti-Spam-Filtered: true X-IronPort-Anti-Spam-Result: AoAIAEZhfU/ZSMDyWmdsb2JhbABFuGgYARYKCggUBCSCCQEBBAEnCwFGBQsLISUPAQQoBxoTh38BCQmxEh+KC5BaBJZ8hEyNQA X-IronPort-AV: E=Sophos;i="4.75,374,1330902000"; d="scan'208";a="139158013" Received: from fmmailgate04.web.de ([217.72.192.242]) by mail4-smtp-sop.national.inria.fr with ESMTP; 05 Apr 2012 11:10:49 +0200 Received: from moweb002.kundenserver.de (moweb002.kundenserver.de [172.19.20.108]) by fmmailgate04.web.de (Postfix) with ESMTP id 279BC74ABBBE for ; Thu, 5 Apr 2012 11:05:25 +0200 (CEST) Received: from frosties.localnet ([95.208.118.96]) by smtp.web.de (mrweb002) with ESMTPA (Nemesis) id 0ML8F7-1SFReE3S2j-000JBW; Thu, 05 Apr 2012 11:05:23 +0200 Received: from mrvn by frosties.localnet with local (Exim 4.77) (envelope-from ) id 1SFid9-0001aV-3U; Thu, 05 Apr 2012 11:05:23 +0200 From: Goswin von Brederlow To: Pierre Chopin Cc: caml-list@inria.fr References: Date: Thu, 05 Apr 2012 11:05:22 +0200 In-Reply-To: (Pierre Chopin's message of "Wed, 4 Apr 2012 16:25:48 -0400") Message-ID: <874nsy1rcd.fsf@frosties.localnet> User-Agent: Gnus/5.110009 (No Gnus v0.9) XEmacs/21.4.22 (linux, no MULE) MIME-Version: 1.0 Content-Type: text/plain; charset=iso-8859-1 Content-Transfer-Encoding: 8bit X-Provags-ID: V02:K0:QeooukzIWABnTDr7BKga+DiowP1sKfMGtYEeZ9+fH5c a2+KRDvSZUt0b6UB6Pm+URQkaDJ2GfhGkjaXcwvzLrpIT+e1mF II30nsweYQ2HbvDFU1b1UMgmbT3wYWUs2tOqLXUvqD325MITjI ROFy90yt/vb+cdwAgpDlDuMOvkKJtOblnmMnpFkAI8ua5OcSXL RPoAfPmKQE/ALfchrbFrQ== Subject: Re: [Caml-list] exn vs option 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