[Haskell-cafe] Efficiency of passing function arguments

Viktor Dukhovni ietf-dane at dukhovni.org
Sat Apr 4 19:03:03 UTC 2020


On Sat, Apr 04, 2020 at 11:31:41AM +0300, Georgi Lyubenov wrote:

> For example, consider this idList:
> ```
> idList :: [a] -> [a]
> idList [] = []
> idList (x:xs) = x : idList xs
> ```
> Semantically this is the same as (a stricter) id, but we're deconstructing
> values and then constructing other ones (putting them in new boxes), so in
> effect, we're "copying" the input list.

But the function is not in fact strict, and so if one uses the value of
"idList xs" no more than once to traverse the list (print it, fold it, ...)
the list *as a whole* is never copied, and total memory used remains small
even for very long lists (that are lazily generated).

That said, interposing idList betweent a fold and the input list
increates the amount of GC work that ultimately takes place to cleanup
the per-element thunks.  The elements themselves are not copied, but the
enclosing lazy thunks are somewhat larger as a result of hiding the real
list behind idList.

> I'm not sure if ghc will do some magic optimisation to transform this
> particular function into an operation with no copying, but you can imagine
> something more complicated like map/foldr/reverse etc where it isn't
> possible (although other stuff like fusion is, to eliminate intermediate
> results from composed maps for example).

Construction of copies of lists typically happens when you traverse them
more than once.  Otherwise you just force each element in turn (possibly
via a more complex thunk that still produces that element).

-- 
    Viktor.

--- Folding 100 million Ints without idList:

       8,000,045,248 bytes allocated in the heap
             771,488 bytes copied during GC
              44,504 bytes maximum residency (2 sample(s))
              29,224 bytes maximum slop
                   0 MB total memory in use (0 MB lost due to fragmentation)

                                         Tot time (elapsed)  Avg pause  Max pause
      Gen  0      7658 colls,     0 par    0.022s   0.022s     0.0000s    0.0018s
      Gen  1         2 colls,     0 par    0.001s   0.001s     0.0004s    0.0007s

      INIT    time    0.001s  (  0.001s elapsed)
      MUT     time    0.981s  (  0.982s elapsed)
      GC      time    0.023s  (  0.023s elapsed)
      EXIT    time    0.000s  (  0.000s elapsed)
      Total   time    1.005s  (  1.005s elapsed)

      %GC     time       0.0%  (0.0% elapsed)

      Alloc rate    8,153,859,656 bytes per MUT second

      Productivity  97.6% of total user, 97.7% of total elapsed

--- Folding 100 million Ints with idList:

      12,800,039,208 bytes allocated in the heap
           1,701,952 bytes copied during GC
              44,504 bytes maximum residency (2 sample(s))
              29,224 bytes maximum slop
                   0 MB total memory in use (0 MB lost due to fragmentation)

                                         Tot time (elapsed)  Avg pause  Max pause
      Gen  0     12206 colls,     0 par    0.030s   0.029s     0.0000s    0.0007s
      Gen  1         2 colls,     0 par    0.001s   0.001s     0.0004s    0.0007s

      INIT    time    0.000s  (  0.000s elapsed)
      MUT     time    1.484s  (  1.485s elapsed)
      GC      time    0.031s  (  0.030s elapsed)
      EXIT    time    0.000s  (  0.000s elapsed)
      Total   time    1.516s  (  1.516s elapsed)

      %GC     time       0.0%  (0.0% elapsed)

      Alloc rate    8,622,777,676 bytes per MUT second

      Productivity  97.9% of total user, 98.0% of total elapsed



More information about the Haskell-Cafe mailing list