From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Original-To: caml-list@sympa.inria.fr Delivered-To: caml-list@sympa.inria.fr Received: from mail3-relais-sop.national.inria.fr (mail3-relais-sop.national.inria.fr [192.134.164.104]) by sympa.inria.fr (Postfix) with ESMTPS id CDCD27F615 for ; Mon, 19 Dec 2016 16:48:51 +0100 (CET) Authentication-Results: mail3-smtp-sop.national.inria.fr; spf=None smtp.pra=ivg@ieee.org; spf=Pass smtp.mailfrom=ivg@ieee.org; spf=None smtp.helo=postmaster@mail-lf0-f54.google.com Received-SPF: None (mail3-smtp-sop.national.inria.fr: no sender authenticity information available from domain of ivg@ieee.org) identity=pra; client-ip=209.85.215.54; receiver=mail3-smtp-sop.national.inria.fr; envelope-from="ivg@ieee.org"; x-sender="ivg@ieee.org"; x-conformance=sidf_compatible Received-SPF: Pass (mail3-smtp-sop.national.inria.fr: domain of ivg@ieee.org designates 209.85.215.54 as permitted sender) identity=mailfrom; client-ip=209.85.215.54; receiver=mail3-smtp-sop.national.inria.fr; envelope-from="ivg@ieee.org"; x-sender="ivg@ieee.org"; x-conformance=sidf_compatible; x-record-type="v=spf1" Received-SPF: None (mail3-smtp-sop.national.inria.fr: no sender authenticity information available from domain of postmaster@mail-lf0-f54.google.com) identity=helo; client-ip=209.85.215.54; receiver=mail3-smtp-sop.national.inria.fr; envelope-from="ivg@ieee.org"; x-sender="postmaster@mail-lf0-f54.google.com"; x-conformance=sidf_compatible IronPort-PHdr: =?us-ascii?q?9a23=3ApEzbyhBxcLOAU97Dr6t6UyQJP3N1i/DPJgcQr6Af?= =?us-ascii?q?oPdwSPv8psbcNUDSrc9gkEXOFd2CrakV0KyJ7Ou/BiQp2tWoiDg6aptCVhsI24?= =?us-ascii?q?09vjcLJ4q7M3D9N+PgdCcgHc5PBxdP9nC/NlVJSo6lPwWB6nK94iQPFRrhKAF7?= =?us-ascii?q?Ovr6GpLIj8Swyuu+54Dfbx9GiTe5b75+Nhe7oAfeusQUg4ZpN7o8xAbOrnZUYe?= =?us-ascii?q?pd2HlmJUiUnxby58ew+IBs/iFNsP8/9MBOTLv3cb0gQbNXEDopPWY15Nb2tRbY?= =?us-ascii?q?VguA+mEcUmQNnRVWBQXO8Qz3UY3wsiv+sep9xTWaMMjrRr06RTiu86FmQwLuhS?= =?us-ascii?q?waNTA27XvXh9Ryg6JVoByvqR9xzZPKbo6JL/dxZL/RcMkASGZdQspcVSpMCZ68?= =?us-ascii?q?YYsVCOoBOP5VoYrjp1QUqxu1GAiiBOTzyj9PmH/5wa060+U9EQHdwQctGNMOsG?= =?us-ascii?q?rbrNjuNacdT/66w7fSwTXEdfNW1i7w5Y7VeR4vpvGMWKh/ccvXyUQ3GAPKkE+Q?= =?us-ascii?q?ppH8MzOOyuQNtGyb7/JlVe21jW4nrRt9rSWxycs0lobGnIcVylTY+iV43IY0Jc?= =?us-ascii?q?e0SElhYd6rFpZbqiKUN5NuT888X21lvDw2x74GtJKhYiQG1ZQqywTfZvGIdYWD?= =?us-ascii?q?/wjtW/yLIThigXJoYLK/iAi28Uin0uD8U9O70FdOriZcltnMuGwB2wXd6sWHRf?= =?us-ascii?q?Zx5Eih2TGI1wDc7uFLP1o4mrbcK54k2rIwl5wTvlrfHiLuhkn6kKubel8n9+Wo?= =?us-ascii?q?8ejrfKjqq5+GO4J0hQzyKqEulda+AeQ8PAgORW+b+eGk2b3i4035T65Hjvwona?= =?us-ascii?q?bHrp/WP8MbprS2AwNNyIYs9w6/Dyu60NQfhXQIMEhKeBeDj4TwP1HOIev4Deuk?= =?us-ascii?q?jlS3kDZrwujGMaf7DpXMKHjDirbhcqxn505S0gpghexYsr1ZEL1JG+70Xlf0vd?= =?us-ascii?q?reRks4NQKz6+nqEtk4zZ8ZXXqKC6mfdq/f5wym/OUqdsiSbYldlzHhLOYu5//y?= =?us-ascii?q?ljdtmEESVaik0JZRb2q3SKc1a36FaGbh149SWVwBuRAzGbTn?= X-IronPort-Anti-Spam-Filtered: true X-IronPort-Anti-Spam-Result: =?us-ascii?q?A0A+AACUAFhYhjbXVdFdGgEBAQECAQEBA?= =?us-ascii?q?QgBAQEBFQEBAQECAQEBAQgBAQEBgwwBAQEBAT86gQYHjUiWWZUOggomhXwCgXs?= =?us-ascii?q?HPxQBAQEBAQEBAQEBARIBAQEICwsJHTCCMxiCHgEEASMdAQETJAEECwkCCwMqC?= =?us-ascii?q?gICIhIBBQEcBhMJiFoIDolqkQs/ixpogiiDDAEBBYd6AQEBAQEFAQEBAQEBARk?= =?us-ascii?q?IEoYkhFmETIJ4gl2IWQ6HHIkwgUKNRYNvkE2IDoYLgkkUHoEUH4FcKxMDgwwsI?= =?us-ascii?q?IIGIDQBhkwqghMBAQE?= X-IPAS-Result: =?us-ascii?q?A0A+AACUAFhYhjbXVdFdGgEBAQECAQEBAQgBAQEBFQEBAQE?= =?us-ascii?q?CAQEBAQgBAQEBgwwBAQEBAT86gQYHjUiWWZUOggomhXwCgXsHPxQBAQEBAQEBA?= =?us-ascii?q?QEBARIBAQEICwsJHTCCMxiCHgEEASMdAQETJAEECwkCCwMqCgICIhIBBQEcBhM?= =?us-ascii?q?JiFoIDolqkQs/ixpogiiDDAEBBYd6AQEBAQEFAQEBAQEBARkIEoYkhFmETIJ4g?= =?us-ascii?q?l2IWQ6HHIkwgUKNRYNvkE2IDoYLgkkUHoEUH4FcKxMDgwwsIIIGIDQBhkwqghM?= =?us-ascii?q?BAQE?= X-IronPort-AV: E=Sophos;i="5.33,374,1477954800"; d="scan'208,217";a="205122902" Received: from mail-lf0-f54.google.com ([209.85.215.54]) by mail3-smtp-sop.national.inria.fr with ESMTP/TLS/AES128-GCM-SHA256; 19 Dec 2016 16:48:50 +0100 Received: by mail-lf0-f54.google.com with SMTP id y21so56782991lfa.1 for ; Mon, 19 Dec 2016 07:48:50 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ieee-org.20150623.gappssmtp.com; s=20150623; h=mime-version:in-reply-to:references:from:date:message-id:subject:to :cc; bh=ghCr55JiHX6W8fNadIqXn7/yG7O1w2M0+CGW7PDwyY0=; b=m+33Bd65zSF/mPyVfjLCOljDP5rrb7DvLBHD68buMhwG+UgacJVBtPLyngec6fLMIz KvAQTgqzn30CKbq859ySba+X+PxkSlK0i9O+SKC2CbouUk3FYaRKqtxMUT8M6TLhc/Sq SotiYcKfV3soY9mJrWP7muU2m9t/EyQ1ZMMYuQdNaAgfTeg0ZG8epxNEfavp9i+PZ3KT g5qpCKy+32qA6rEgvoX8OcYVrvhXGKHXzJluUCfOGaGlvnla2aeHRiSd8z2rOokhLABT gmGgv2g/ixaPVqwd+hVUGKGpPSHKMsrJ9JEWj7jTE5rwGeNFKdJVCIUE43YDfpph1ilE vH5A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:in-reply-to:references:from:date :message-id:subject:to:cc; bh=ghCr55JiHX6W8fNadIqXn7/yG7O1w2M0+CGW7PDwyY0=; b=DaFD8olKxlLD8XXZPfVJRGK/wZ9Nmu1A7BGQoKxR1xByHQSDtWYYvv/JnkE6JAbEm3 owB4SWNY1+X/rfUNvYx05akraq+PdjqRGQ+o77MKgflwO5tmDDXvBV6Rw9V6Vh/OePqc uk1l9VuQbtxcItGp3gAvkG/qCWLE+xAs/uphJDftRCfoLoUg8E1P68QPRyPuTkUN+Gav gFbkdS3cUV4gUvGw8RJ+9DzVO92Ub8zezVVMW71D4rMd8s+SNIrTXY5rtfAB/aCRnOzn S4pAy93npnKWc6dAX6smV7z3Gr/mRQepkWqw748BxodEmzj6WFupXBhVVZzreFCOdNrE AypA== X-Gm-Message-State: AIkVDXImOFwINorvhxlEYY7T3djjqlupuD6EN/FnoLwZzLcbuQJHGtVCI9MPeuhYlcrjo7fFKPTWHydVxNQmnzFJ X-Received: by 10.46.76.1 with SMTP id z1mr7587108lja.41.1482162528888; Mon, 19 Dec 2016 07:48:48 -0800 (PST) MIME-Version: 1.0 Received: by 10.114.93.99 with HTTP; Mon, 19 Dec 2016 07:48:48 -0800 (PST) In-Reply-To: <1482148297.4629.19.camel@gerd-stolpmann.de> References: <7bc766a2-d460-524b-35ca-89609a34b719@tu-berlin.de> <1482148297.4629.19.camel@gerd-stolpmann.de> From: Ivan Gotovchits Date: Mon, 19 Dec 2016 10:48:48 -0500 Message-ID: To: Gerd Stolpmann Cc: =?UTF-8?Q?Christoph_H=C3=B6ger?= , caml-list Content-Type: multipart/alternative; boundary=f403045ea6da08c55a054404db07 Subject: Re: [Caml-list] Closing the performance gap to C --f403045ea6da08c55a054404db07 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: quoted-printable Hi Christoph, The problem is that your function definitions, like `loop` and `rk4_step`, have too many parameters and OCaml is not able to eliminate them and is actually not trying. It was always a feature of OCaml that poorly written code will be compiled into a poorly written program. OCaml never tries to fix programmer's errors. It will try to minimize the abstraction overhead (often to the zero). But if the abstractions, on the first hand, were chosen incorrectly, then it won't fix the code. In your particular example, the C compiler was quite aggressive and was capable of eliminating unnecessary computations. I wouldn't, however, assume that the C compiler will be always able to do this for you. Let's go through the code: let rec loop steps h n y t =3D if n < steps then loop steps h (n+1) (rk4_step y t h) (t +. h) else y Here variables `steps` and `h` are loop invariants, so you shouldn't put it into the loop function parameter list. Well yes, a compiler can find out loop invariants and remove them for you. But in our case, to remove them, it will need to change the type of the function. The compiler will not go that far for us. It respects a programmer, and if a programmer decided to make his loop function to depend on `6` arguments, then it means that the computation is actually depending on 6 arguments. So, it will allocate 6 registers to hold loop variables, with all the consequences. Now, let's take a look at the `rk4_step` function: let rk4_step y t h =3D let k1 =3D h *. y' t y in let k2 =3D h *. y' (t +. 0.5*.h) (y +. 0.5*.k1) in let k3 =3D h *. y' (t +. 0.5*.h) (y +. 0.5*.k2) in let k4 =3D h *. y' (t +. h) (y +. k3) in y +. (k1+.k4)/.6.0 +. (k2+.k3)/.3.0 This function, is, in fact, a body of the loop, and everything except t is loop invariant here. Moreover, function `y'` is defined as: let y' t y =3D cos t I.e., it doesn't really use it the second argument. Probably, a compiler should inline the call, and eliminate lots of unecessary computations, and thus free a few registers, but, apparently, OCaml doesn't do this (even in 4.03+flambda). So we should do this manually: let rk4_step y t =3D let k1 =3D h *. y' t in let k2 =3D h *. y' (t +. 0.5*.h) in let k3 =3D h *. y' (t +. 0.5*.h) in let k4 =3D h *. y' (t +. h) (y +. k3) in y +. (k1+.k4)/.6.0 +. (k2+.k3)/.3.0 We can even see, that `k3` and `k2` are equal now, so we can eliminate them: let rk4_step y t =3D let k1 =3D h *. y' t in let k2 =3D h *. y' (t +. 0.5*.h) in let k4 =3D h *. y' (t +. h) (y +. k3) in y +. (k1+.k4)/.6.0 +. k2 *. 1.5 Finally, we don't want to pass `y` into the `rk4_step` every time, as we don't want to require an extra register for it. After all these manual optimizations, we have the following program: let h =3D 0.1 let exact t =3D sin t let rk4_step t =3D let k1 =3D h *. cos t in let k2 =3D h *. cos (t +. 0.5*.h) in let k4 =3D h *. cos (t +. h) in (k1+.k4)/.6.0 +. k2*.1.5 let compute steps =3D let rec loop n y t =3D if n < steps then loop (n+1) (y +. rk4_step t) (t +. h) else y in loop 1 1.0 0.0 let () =3D let y =3D compute 102 in let err =3D abs_float (y -. (exact ((float_of_int 102) *. h))) in let large =3D 50000000 in let y =3D compute large in Printf.printf "%b\n" (abs_float (y -. (exact (float_of_int large) *. h)) < 2. *. err) This program has the same performance as the C one... unless I pass really aggressive optimization options to the C compiler, that will emit a platform specific code, e.g., gcc rk.c -lm -O3 -march=3Dcorei7-avx -o rksse These options basically double the performance of the C version, leaving OCaml lagging behind. That is because, OCaml, obviously, cannot follow the latest developments of intel CPU, especially in the field of SSE. The fun part is that when I've tried to compile the same file with clang, the resulting program was even slower than the original non-optimized OCaml. But this is all micro benchmarking of course, so don't jump to fast conclusions (although I like to think that OCaml is faster than Clang :)) As a final remark, my experience in HPC shows that in general you should not really rely on compiler optimizations and hope that the compiler will do the magic for you. Even the GCC compiler. It would be very easy to accidentally amend the above program in a such way, that the optimizations will no longer fire in. Of course, writing in assembly is also not a choice. If you really need to optimize, then you should find out the performance bottleneck and then optimize it manually until you get an expected performance. Alternatively, you can use plain old Fortran to get the reliable performance. And then call it from C or OCaml. Best wishes, Ivan On Mon, Dec 19, 2016 at 6:51 AM, Gerd Stolpmann wrote: > Hi Christoph, > > the extra code looks very much like an allocation on the minor heap: > > sub $0x10,%r15 > lea 0x25c7b6(%rip),%rax > cmp (%rax),%r15 > jb 404a8a > lea 0x8(%r15),%rax > movq $0x4fd,-0x8(%rax) > > r15 points to the used area of the minor heap - by decrementing it you > get an additional block of memory. It is compared against the beginning > of the heap to check whether GC is needed. The constant 0x4fd is the > header of the new block (which must be always initialized). > > From the source code, it remains unclear for what this is used. > Obviously, the compiler runs out of registers, and moves some values to > the minor heap (temporarily). When you call a C function like cos it is > likely that this happens because the C calling conventions do not > preserve the FP registers (xmm*). This could be improved if the OCaml > compiler tried alternate places for temporarily storing FP values: > > - int registers (which is perfectly possible on 64 bit platforms). > A number of int registers survive C calls. > - stack > > To my knowledge, the OCaml compiler never tries this (but this could be > out of date). This is a fairly specific optimization that makes mostly > sense for purely iterating or aggregating functions like yours that do > not store FP values away. > > Gerd > > Am Samstag, den 17.12.2016, 14:02 +0100 schrieb Christoph H=C3=B6ger: > > Ups. Forgot the actual examples. > > > > Am 17.12.2016 um 14:01 schrieb Christoph H=C3=B6ger: > > > > > > Dear all, > > > > > > find attached two simple runge-kutta iteration schemes. One is > > > written > > > in C, the other in OCaml. I compared the runtime of both and gcc (- > > > O2) > > > produces an executable that is roughly 30% faster (to be more > > > precise: > > > 3.52s vs. 2.63s). That is in itself quite pleasing, I think. I do > > > not > > > understand however, what causes this difference. Admittedly, the > > > generated assembly looks completely different, but both compilers > > > inline > > > all functions and generate one big loop. Ocaml generates a lot more > > > scaffolding, but that is to be expected. > > > > > > There is however an interesting particularity: OCaml generates 6 > > > calls > > > to cos, while gcc only needs 3 (and one direct jump). Surprisingly, > > > there are also calls to cosh, acos and pretty much any other > > > trigonometric function (initialization of constants, maybe?) > > > > > > However, the true culprit seems to be an excess of instructions > > > between > > > the different calls to cos. This is what happens between the first > > > two > > > calls to cos: > > > > > > gcc: > > > jmpq 400530 > > > nop > > > nopw %cs:0x0(%rax,%rax,1) > > > > > > sub $0x38,%rsp > > > movsd %xmm0,0x10(%rsp) > > > movapd %xmm1,%xmm0 > > > movsd %xmm2,0x18(%rsp) > > > movsd %xmm1,0x8(%rsp) > > > callq 400530 > > > > > > ocamlopt: > > > > > > callq 401a60 > > > mulsd (%r12),%xmm0 > > > movsd %xmm0,0x10(%rsp) > > > sub $0x10,%r15 > > > lea 0x25c7b6(%rip),%rax > > > cmp (%rax),%r15 > > > jb 404a8a > > > lea 0x8(%r15),%rax > > > movq $0x4fd,-0x8(%rax) > > > > > > movsd 0x32319(%rip),%xmm1 > > > > > > movapd %xmm1,%xmm2 > > > mulsd %xmm0,%xmm2 > > > addsd 0x0(%r13),%xmm2 > > > movsd %xmm2,(%rax) > > > movapd %xmm1,%xmm0 > > > mulsd (%r12),%xmm0 > > > addsd (%rbx),%xmm0 > > > callq 401a60 > > > > > > > > > Is this caused by some underlying difference in the representation > > > of > > > numeric values (i.e. tagged ints) or is it reasonable to attack > > > this > > > issue as a hobby experiment? > > > > > > > > > thanks for any advice, > > > > > > Christoph > > > > > > -- > ------------------------------------------------------------ > Gerd Stolpmann, Darmstadt, Germany gerd@gerd-stolpmann.de > My OCaml site: http://www.camlcity.org > Contact details: http://www.camlcity.org/contact.html > Company homepage: http://www.gerd-stolpmann.de > ------------------------------------------------------------ > > > --f403045ea6da08c55a054404db07 Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: quoted-printable
Hi Christoph,

The problem=C2=A0is that = your function definitions, like `loop` and `rk4_step`, have too many parame= ters
and OCaml is not able to eliminate them=C2=A0and is actually= not trying. It was always a feature of OCaml
that poorly written= code will be compiled into a poorly written program. OCaml never tries to = fix
programmer's errors. It will try to minimize the abstract= ion overhead (often to the zero). But if the abstractions,
on the= first hand, were chosen incorrectly, then it won't fix the code.=C2=A0=

In your particular example, the C compiler was qu= ite aggressive and was capable of eliminating unnecessary=C2=A0computations= .=C2=A0
I wouldn't, however, assume that the C compiler will = be always able to do this for you.=C2=A0

Let's= go through the code:


let rec loop steps h n y t =3D
=
=C2=A0 if n < steps then l= oop steps h (n+1) (rk4_step y t h) (t +. h) else
=C2=A0 =C2=A0 y

Here variables `steps` and `h` are loop invariants= , so you shouldn't put it into the loop function parameter list.=C2=A0<= div>Well yes, a compiler can find out loop invariants=C2=A0and remove them = for you. But in our case, to remove them,=C2=A0
it will need to c= hange the type of the function. The=C2=A0compiler will not go that far for = us. It respects a programmer, and if=C2=A0a=C2=A0
programmer decided t= o make his loop function to depend on `6` arguments, then it means that the= computation=C2=A0is actually depending
on 6 arguments. So, it will all= ocate 6 registers to hold loop variables, with all the consequences.
<= div>

Now, let's take a look at the `rk4_st= ep` function:

= let rk4_step y t h =3D
=C2=A0 let k1 =3D h *. y' t y in
=C2=A0 let k2 =3D h *. y' (t +. 0= .5*.h) (y +. 0.5*.k1) in
=C2=A0 let k3 =3D h *. y' (t +. 0.5*.h) (y +. 0.5*.k2) in=
=C2=A0 let= k4 =3D h *. y' (t +. h) (y +. k3) in
=C2=A0 y +. (k1+.k4)/.6.0 +. (k2+.k3)/.3.0


This funct= ion, is, in fact, a body of the loop, and everything except t is loop invar= iant here. Moreover,=C2=A0
function =C2=A0`y'` is defined as:=

let y' =
t y =3D cos t
= I.e., it doesn't really use= it the second argument. Probably, a compiler should inline the call, and e= liminate
lots of unecessary computations, and thus= free a few registers, but, apparently, OCaml doesn't do= this
(even in 4.03+flambda).

So we should do this manually:
=
let rk4_step y t =3D
=C2=A0 let k1 =3D h *. y' t in
=C2=A0 let k2 =3D h *. y' (t +. 0.5*.h) =C2=A0in=
=C2=A0 let k3 =3D h *= . y' (t +. 0.5*.h) =C2=A0in
=C2=A0 let k4 =3D h *. y' (t +. h) (y +. k3) in
<= div>=C2=A0 y +. (k1+.k4)/.6.0 +. (k2+.k= 3)/.3.0

We can ev= en see, that `k3` and `k2` are equal now, so we can eliminate them:<= /div>

let rk4_step y t =3D
=C2=A0 let k1 = =3D h *. y' t in
=C2=A0 let k2 =3D h *. y' (t +. 0.5*.h) =C2=A0in
=
=C2=A0 let k4 =3D h *. = y' (t +. h) (y +. k3) in
=C2=A0 y +. (k1+.k4)/.6.0 +. k2 *. 1.5
=


Finally, we don't want to pass `y`= into the `rk4_step` every time, as we don't want to require an extra r= egister for it.
After all these manual optimizations, we have the= following program:

let h =3D= 0.1

<= font face=3D"monospace, monospace">let exact t =3D sin t

let rk4_step t =3D
=C2=A0 let k1 =3D h *. cos t in
=C2=A0 let k2 =3D h *. cos (t +. 0.5*.h) in
=C2=A0 let k4 =3D h *. cos (t +. h) in
=C2=A0 (k1+.k4)/.6.0 +. k2*= .1.5

<= font face=3D"monospace, monospace">let compute steps =3D
=C2=A0 let rec loop n y t =3D
=C2=A0 =C2=A0 if n < steps
=C2=A0 =C2=A0 then loop (n+1) (y = +. rk4_step t) (t +. h)
= =C2=A0 =C2=A0 else y in
= =C2=A0 loop 1 1.0 0.0
let () =3D
=C2=A0 let y =3D compute 102 in
=C2=A0 let err =3D abs_float (= y -. (exact ((float_of_int 102) *. h))) in
=C2=A0 let large =3D 50000000 in
=C2=A0 let y =3D compute large in
<= font face=3D"monospace, monospace">=C2=A0 Printf.printf "%b\n"
=C2=A0 =C2=A0 (abs_float (y= -. (exact (float_of_int large) *. h)) < 2. *. err)
<= div>

This program has the same pe= rformance as the C one... unless I pass really aggressive optimization opti= ons=C2=A0
to the C compiler, that will emit a platform specific c= ode, e.g.,

=C2=A0 =C2=A0 gcc rk.c -lm -O3 -march= =3Dcorei7-avx -o rksse


These op= tions basically double=C2=A0the performance of the C version, leaving=C2=A0= OCaml lagging behind. That is because,=C2=A0
OCaml, obviously, ca= nnot follow the latest developments of intel CPU, especially in the field o= f SSE.=C2=A0

The fun part=C2=A0is that when I'= ve tried to compile the same file with clang, the resulting program was eve= n slower
than the original non-optimized OCaml. But this is all m= icro benchmarking of course, so don't jump to fast conclusions=C2=A0
(although I like to think that OCaml is faster than Clang :))=C2=A0=


As a final remark, my experience i= n HPC shows that in general you should not really rely on compiler optimiza= tions and hope
that the compiler will do the magic for you. Even the G= CC compiler. It would be very easy to accidentally=C2=A0amend the above pro= gram=C2=A0
in a such way, that the optimizations will no longer fire in= . Of course, writing in assembly is also not a choice. If you really needto optimize, then you should find out the performance bottleneck=C2=A0an= d then optimize it manually until you get an expected=C2=A0performance.=C2= =A0
Alternatively, you can use plain old Fortran to get the relia= ble performance. And then call it from C or OCaml.=C2=A0


Best wishes,
Ivan

=

On Mon, Dec 19, 2016 at 6:51 AM, Gerd Stolpmann <= info@gerd-stolp= mann.de> wrote:
Hi Christop= h,

the extra code looks very much like an allocation on the minor heap:

sub=C2=A0=C2=A0=C2=A0=C2=A0$0x10,%r15
lea=C2=A0=C2=A0=C2=A0=C2=A00x25c7b6(%rip),%rax
cmp=C2=A0=C2=A0=C2=A0=C2=A0(%rax),%r15
jb=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0404a8a <dlerror@plt+0x2d0a>
lea=C2=A0=C2=A0=C2=A0=C2=A00x8(%r15),%rax
movq=C2=A0=C2=A0=C2=A0$0x4fd,-0x8(%rax)

r15 points to the used area of the minor heap - by decrementing it y= ou
get an additional block of memory. It is compared against the beginning
of the heap to check whether GC is needed. The constant 0x4fd is the
header of the new block (which must be always initialized).

=46rom the source code, it remains unclear for what this is used.
Obviously, the compiler runs out of registers, and moves some values to
the minor heap (temporarily). When you call a C function like cos it is
likely that this happens because the C calling conventions do not
preserve the FP registers (xmm*). This could be improved if the OCaml
compiler tried alternate places for temporarily storing FP values:

=C2=A0- int registers (which is perfectly possible on 64 bit platforms).
=C2=A0 =C2=A0A number of int registers survive C calls.
=C2=A0- stack

To my knowledge, the OCaml compiler never tries this (but this could be
out of date). This is a fairly specific optimization that makes mostly
sense for purely iterating or aggregating functions like yours that do
not store FP values away.

Gerd

Am Samstag, den 17.12.2016, 14:02 +0100 schrieb Christoph H=C3=B6ger:
> Ups. Forgot the actual examples.
>
> Am 17.12.2016 um 14:01 schrieb Christoph H=C3=B6ger:
> >
> > Dear all,
> >
> > find attached two simple runge-kutta iteration schemes. One is
> > written
> > in C, the other in OCaml. I compared the runtime of both and gcc = (-
> > O2)
> > produces an executable that is roughly 30% faster (to be more
> > precise:
> > 3.52s vs. 2.63s). That is in itself quite pleasing, I think. I do=
> > not
> > understand however, what causes this difference. Admittedly, the<= br> > > generated assembly looks completely different, but both compilers=
> > inline
> > all functions and generate one big loop. Ocaml generates a lot mo= re
> > scaffolding, but that is to be expected.
> >
> > There is however an interesting particularity: OCaml generates 6<= br> > > calls
> > to cos, while gcc only needs 3 (and one direct jump). Surprisingl= y,
> > there are also calls to cosh, acos and pretty much any other
> > trigonometric function (initialization of constants, maybe?)
> >
> > However, the true culprit seems to be an excess of instructions > > between
> > the different calls to cos. This is what happens between the firs= t
> > two
> > calls to cos:
> >
> > gcc:
> > jmpq=C2=A0=C2=A0=C2=A0400530 <cos@plt>
> > nop
> > nopw=C2=A0=C2=A0=C2=A0%cs:0x0(%rax,%rax,1)
> >
> > sub=C2=A0=C2=A0=C2=A0=C2=A0$0x38,%rsp
> > movsd=C2=A0=C2=A0%xmm0,0x10(%rsp)
> > movapd %xmm1,%xmm0
> > movsd=C2=A0=C2=A0%xmm2,0x18(%rsp)
> > movsd=C2=A0=C2=A0%xmm1,0x8(%rsp)
> > callq=C2=A0=C2=A0400530 <cos@plt>
> >
> > ocamlopt:
> >
> > callq=C2=A0=C2=A0401a60 <cos@plt>
> > mulsd=C2=A0=C2=A0(%r12),%xmm0
> > movsd=C2=A0=C2=A0%xmm0,0x10(%rsp)
> > sub=C2=A0=C2=A0=C2=A0=C2=A0$0x10,%r15
> > lea=C2=A0=C2=A0=C2=A0=C2=A00x25c7b6(%rip),%rax
> > cmp=C2=A0=C2=A0=C2=A0=C2=A0(%rax),%r15
> > jb=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0404a8a <dlerror@plt+0x2d0a>=
> > lea=C2=A0=C2=A0=C2=A0=C2=A00x8(%r15),%rax
> > movq=C2=A0=C2=A0=C2=A0$0x4fd,-0x8(%rax)
> >
> > movsd=C2=A0=C2=A00x32319(%rip),%xmm1
> >
> > movapd %xmm1,%xmm2
> > mulsd=C2=A0=C2=A0%xmm0,%xmm2
> > addsd=C2=A0=C2=A00x0(%r13),%xmm2
> > movsd=C2=A0=C2=A0%xmm2,(%rax)
> > movapd %xmm1,%xmm0
> > mulsd=C2=A0=C2=A0(%r12),%xmm0
> > addsd=C2=A0=C2=A0(%rbx),%xmm0
> > callq=C2=A0=C2=A0401a60 <cos@plt>
> >
> >
> > Is this caused by some underlying difference in the representatio= n
> > of
> > numeric values (i.e. tagged ints) or is it reasonable to attack > > this
> > issue as a hobby experiment?
> >
> >
> > thanks for any advice,
> >
> > Christoph
> >
>
--
----------------------------------------------------------= --
Gerd Stolpmann, Darmstadt, Germany=C2=A0 =C2=A0 gerd@gerd-stolpmann.de
My OCaml site:=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 http://www.camlcity.org<= br> Contact details:=C2=A0 =C2=A0 =C2=A0 =C2=A0 http://www.camlcity.= org/contact.html
Company homepage:=C2=A0 =C2=A0 =C2=A0 =C2=A0http://www.gerd-stolpmann.de=
------------------------------------------------------------



--f403045ea6da08c55a054404db07--