title: Slow stream fusion in Data.Vector.Storable.ZipWith but fast in Data.Vector.Unboxed.ZipWith
body: <p>Currently, I found some strange behavior in my function. I have a transformation function that transforms from <code>(x,a,b) -&gt; (x,y,z)</code>. I'm using <a href="https://hackage.haskell.org/package/vector-0.12.3.0/docs/Data-Vector-Storable.html" rel="nofollow noreferrer">Data.Vector.Storable</a> since I'm communicating with some external libs.
  I estimated the time for the transfer function for a test-dataset something below a dozen seconds.
  But my implementation is slow as hell! Here, is some kind-of:</p>
  <pre><code>import Data.Complex
  import System.Random
  import qualified Data.Vector as V
  import qualified Data.Vector.Storable as VS
  
  nx = 128
  ny = 128
  nz = 128
  na = 128
  nb = 16
  
  transform :: VS.Vector (Complex Float) -&gt; VS.Vector (Complex Float)-&gt;VS.Vector (Complex Float)-&gt;VS.Vector (Complex Float)
  transform !inData !transfAArray !transfBArray= VS.concatMap
      (\x -&gt;
         VS.concatMap
           (\y -&gt;
              VS.map
                (\z -&gt;
                   calcSum y z x) $
              VS.enumFromN 0 nz) $
         VS.enumFromN 0 ny ) $
    VS.enumFromN 0 nx
    where
      calcSum :: Int -&gt; Int -&gt; Int -&gt; Complex Float
      calcSum y z x =
        VS.sum $
        VS.map
          (\b -&gt;
             (*) (transfB y b) $ dotProd (transfA z) (inDataSlice x b) ) $
             -- (*) (transfB y b) $ dotProd (transfA z) (transfA z) ) $ Is fast
        VS.enumFromN 0 nb
      dotProd !a1 !a2 = VS.sum $ VS.zipWith (*) a1 a2
      inDataSlice x b = VS.slice (x*na*nb + b*na) na inData
      transfA z =  VS.slice (z * na) na transfAArray
      transfB y b =  VS.unsafeIndex transfBArray (y * nb + b)
  
  
  
  
  
  randomComplex :: IO (Complex Float)
  randomComplex = (:+) &lt;$&gt; randomIO &lt;*&gt; randomIO
  
  main :: IO ()
  main = do
    inData &lt;- VS.generateM (nx * na * nb) (\i -&gt; randomComplex) 
    transfAArray &lt;- VS.generateM (nz * na) (\i -&gt; randomComplex)
    transfBArray &lt;- VS.generateM (ny * nb) (\i -&gt; randomComplex)
  
    let !outData = transform inData transfAArray transfBArray 
    print $ VS.sum outData
  </code></pre>
  <p>It takes minutes to complete this! If I activate line 35 it works very fast - so there must be something with the stream fusion I guess (Profiling shows a lot of &gt;&gt;= in the process). I re-created the code in c++ and got a runtime about 5s (single threaded):</p>
  <pre><code>#include &lt;string&gt;
  #include &lt;iterator&gt;
  #include &lt;iostream&gt;
  #include &lt;algorithm&gt;
  #include &lt;vector&gt;
  #include &lt;complex&gt;
  #include &lt;cmath&gt;
  #include &lt;numeric&gt;
   
  int main()
  {
      // transformation [nx*na*nb] -&gt; [nx*ny*nz]
      // [x][y][z], with dimensions 128 128 128
      const unsigned int nx = 128;
      const unsigned int ny = 128;
      const unsigned int nz = 128;
      std::vector&lt;std::complex&lt;float&gt;&gt; out(nx*ny*nz);
  
      const unsigned int na = 128;
      const unsigned int nb = 16;
      std::vector&lt;std::complex&lt;float&gt;&gt; in(nx * na * nb);
      std::iota (std::begin(in), std::end(in), 0);
  
      std::vector&lt;std::complex&lt;float&gt;&gt; transA(na * nz);
      std::iota (std::begin(transA), std::end(transA), 0);
      std::vector&lt;std::complex&lt;float&gt;&gt; transB(nb * ny);
      std::iota (std::begin(transB), std::end(transB), 0);
  
      for (int x = 0; x &lt; nx; x++) {
          for (int y = 0; y &lt; ny; y++) {
              for (int z = 0; z &lt; nz; z++) {
                  for (int b = 0; b &lt; nb; b++) {
                      std::complex&lt;float&gt; sum = 0;
                      for (int a = 0; a &lt; na; a++) {
                          sum += transA[z*na + a] * in[x*na*nb+b*na+a];
                      }
                      out[x*ny*nz + y*nz + z]= sum * transB[b * ny + y];
                  }
              }
          }
      }
  
      std::cout &lt;&lt; out[12] &lt;&lt; &quot;done\n&quot;;
  }
  </code></pre>
  <p>During my last days of despair I just found the following strange behaviour:
  If I replace Data.Vector.Storable with Data.Vector.Unboxed, it runs as fast as the c++ code!</p>
  <p>Soo.. why?</p>
  <ol>
  <li>As far as I did understand, unboxed and storable are both using successive memory regions - so there should be no problem with Cache/Memory-Access times.</li>
  <li>In the Storable-version TB for heap was allocated, while the Unboxed-version just uses a few MB. I've the feeling that the Storable version is moving the chunks around in memory constantly.</li>
  </ol>
  <p>Any ideas? And is there a simple way to achieve similar speeds with Storage?
  (I'm using ghc-8.10.4 from Stack)</p>
  <p>(PS: Always compiled with -O2 and sometimes also with -fllvm -optlo-O2 :)</p>