<html>
<head>
<style><!--
.hmmessage P
{
margin:0px;
padding:0px
}
body.hmmessage
{
font-size: 10pt;
font-family:Tahoma
}
--></style></head>
<body class='hmmessage'><div dir='ltr'>
Wow, now performance is on par with Java ;)<div>So slow division was main problem, that and GC .</div><div><br></div><div>Thanks!</div><div><br><br><div><div id="SkyDrivePlaceholder"></div>&gt; From: daniel.is.fischer@googlemail.com<br>&gt; To: haskell-cafe@haskell.org<br>&gt; CC: bmaxa@hotmail.com<br>&gt; Subject: Re: [Haskell-cafe] Why is boxed mutable array so slow?<br>&gt; Date: Sat, 1 Dec 2012 21:12:29 +0100<br>&gt; <br>&gt; On Samstag, 1. Dezember 2012, 16:09:05, Branimir Maksimovic wrote:<br>&gt; &gt; All in all even unboxed array is about 10 times slower than Java version.<br>&gt; &gt; I don't understand why is even unboxed array so slow.<br>&gt; <br>&gt; It's not the unboxed arrays that are slow.<br>&gt; <br>&gt; Your code has a couple of weak spots, and GHC's native code generator has a <br>&gt; weakness that bites here.<br>&gt; <br>&gt; On my box, I don't quite have a 10× difference to my translation to Java, it's <br>&gt; a bit less than 7× (0.82s vs 0.12s - I don't want to bring my box to its knees <br>&gt; by running something that takes 3GB+ of RAM, so I run the unboxed array part <br>&gt; only) with the LLVM backend and 8× (0.93s) with the native code generator. <br>&gt; That's in the same ballpark, though.<br>&gt; <br>&gt; So what's the deal?<br>&gt; <br>&gt; Main.main_$s$wa1 [Occ=LoopBreaker]<br>&gt;   :: GHC.Prim.Int#<br>&gt;      -&gt; GHC.Prim.Int#<br>&gt;      -&gt; GHC.Prim.State# GHC.Prim.RealWorld<br>&gt;      -&gt; GHC.Types.Int<br>&gt;      -&gt; GHC.Types.Int<br>&gt;      -&gt; GHC.Types.Int<br>&gt;      -&gt; ...<br>&gt; <br>&gt; Your loops carry boxed Ints around, that's always a bad sign. In this case it <br>&gt; doesn't hurt too much, however, since these values are neither read nor <br>&gt; substituted during the loop (they're first and last index of the array and <br>&gt; number of elements). Additionally, they carry an IOUArray constructor around. <br>&gt; That is unnecessary. Eliminating a couple of dead parameters<br>&gt; <br>&gt; <br>&gt; init' a = do<br>&gt;     (_,n) &lt;- getBounds a<br>&gt;     let init k<br>&gt;           | k &gt; n     = return ()<br>&gt;           | otherwise = do<br>&gt;               let x = fromIntegral $ k + k `div` 3<br>&gt;               unsafeWrite a k x<br>&gt;               init (k+1)<br>&gt;     init 0<br>&gt; <br>&gt; partial_sum a = do<br>&gt;     (_,n) &lt;- getBounds a<br>&gt;     let ps i s<br>&gt;           | i &gt; n     = return ()<br>&gt;           | otherwise = do<br>&gt;               k &lt;- unsafeRead a i<br>&gt;               let l = s + k<br>&gt;               unsafeWrite a i l<br>&gt;               ps (i+1) l<br>&gt;     k &lt;- unsafeRead a 0<br>&gt;     ps 1 k<br>&gt; <br>&gt; brings the time for the native code generator down to 0.82s, and for the LLVM <br>&gt; backend the time remains the same.<br>&gt; <br>&gt; Next problem, you're using `div` for the division.<br>&gt; <br>&gt; `div` does some checking and potentially fixup (not here, since everything is <br>&gt; non-negative) after the machine division because `div` is specified to satisfy<br>&gt; <br>&gt; a = (a `div` b) * b + (a `mod` b)<br>&gt; <br>&gt; with 0 &lt;= a `mod` b &lt; abs b.<br>&gt; <br>&gt; That is in itself slower than the pure machine division you get with quot.<br>&gt; <br>&gt; So let's see what we get with `quot`.<br>&gt; <br>&gt; 0.65s with the native code generator, and 0.13 with the LLVM backend.<br>&gt; <br>&gt; Whoops, what's that?<br>&gt; <br>&gt; The problem is, as can be seen by manually replacing k `quot` 3 with<br>&gt; <br>&gt; (k *2863311531) `shiftR` 33<br>&gt; <br>&gt; (requires 64-bit Ints; equivalent in Java: k*28..1L &gt;&gt; 33), when the native <br>&gt; backend, the LLVM backend and Java (as well as C) all take more or less the <br>&gt; same time [well, the NCG is a bit slower than the other two, 0.11s, 0.11s, <br>&gt; 0.14s], that division is a **very** slow operation.<br>&gt; <br>&gt; Java and LLVM know how to replace the division by the constant 3 with a <br>&gt; mulitplication, a couple of shifts and an addition (since we never have <br>&gt; negative numbers here, just one multiplication and shift suffice, but neither <br>&gt; Java nor LLVM can do that on their own because it's not guaranteed by the <br>&gt; type). The native code generator doesn't - not yet.<br>&gt; <br>&gt; So the programme spends the majority of the time dividing. The array reads and <br>&gt; writes are on par with Java's (and, for that matter, C's).<br>&gt; <br>&gt; If you make the divisor a parameter instead of a compile time constant, the <br>&gt; NCG is not affected at all, the LLVM backend gives you equal performance (it <br>&gt; can't optimise a division by a divisor it doesn't know). Java is at an <br>&gt; advantage there, after a while the JIT sees that it might be a good idea to <br>&gt; optimise the division and so its time only trebles.<br></div></div>                                               </div></body>
</html>