Skip to content

Optimization opportunity: hidden identity functions.

Motivation

See my discourse thread for the background story.

I am investigating if stream fusion could replace foldr/build fusion in base. During the investigation I've encountered bad benchmark results for the streamed drop function: it is up to 20x slower than Prelude.drop. I've looked at the core and found that the streamed drop boiles down to a function like this:

drop :: Int -> [a] -> [a]
drop 0 [] = []
drop 0 (x:xs) = x : drop 0 xs -- [1]
drop n [] = []
drop n (x:xs) = drop (n - 1) xs

The main difference with Prelude.drop is that this function walks over the remainder of the list that it does not drop (see the recursive call at [1]). That is extremely inefficient. In my benchmarks it is up to 20x slower than Prelude.drop because of the extra allocation and subsequent copying of the remainder of the list after dropping.

Proposal

I think this function could be optimized further. First of all, SpecConstr could split it into two:

drop :: Int -> [a] -> [a]
drop 0 [] = []
drop 0 (x:xs) = x : drop0 xs
drop n [] = []
drop n (x:xs) = drop (n - 1) xs

drop0 :: [a] -> [a]
drop0 [] = []
drop0 (x:xs) = x : drop0 xs

SpecConstr doesn't do this optimization yet, but #22781 tracks that issue.

Then a special optimization pass could recognize that drop0 is an identity function and optimize it further to:

drop :: Int -> [a] -> [a]
drop 0 [] = []
drop 0 (x:xs) = x : drop0 xs
drop n [] = []
drop n (x:xs) = drop (n - 1) xs

drop0 :: [a] -> [a]
drop0 xs = xs

This optimization could simply look for the pattern:

<f> xs =
  case xs of
    [] -> []
    x : xs -> x : <f> xs

In Core and replace that with <f> xs = xs.

It also shouldn't be hard to generalize that for any ADT.

Then finally the result of that optimization already simplifies to (modulo unboxing):

drop :: Int -> [a] -> [a]
drop 0 xs = xs
drop n [] = []
drop n (x:xs) = drop (n - 1) xs

Which is what we want.

Edited by Jaro Reinders
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information