Skip to content
Browse files

Turn O(n^2) reverse into O(n)

This patch improves `cabal update` performance by 10x(!) on my system.
Due to this issue, `cabal update` was using an extreme amount of CPU,
while it should be I/O-bound.  The reverse was taking 79s, now it
takes 0.01s.
1 parent 54cf001 commit b9bd0a08fa09c6403f91422e3b23f08d339612eb @nominolo committed
Showing with 1 addition and 1 deletion.
  1. +1 −1 Network/HTTP/Base.hs
2 Network/HTTP/Base.hs
@@ -868,7 +868,7 @@ hopefulTransfer bufOps readL strs
= readL >>=
either (\v -> return $ Left v)
(\more -> if (buf_isEmpty bufOps more)
- then return (Right ([],foldr (flip (buf_append bufOps)) (buf_empty bufOps) strs))
+ then return (Right ([], buf_concat bufOps $ reverse strs))
else hopefulTransfer bufOps readL (more:strs))
-- | A necessary feature of HTTP\/1.1

10 comments on commit b9bd0a0




Wow. A classic "oops" in the world of monoids.


Thumb up


i hate flip, every time i see someone removing flip from code i have to add :+1:




(Saw this on Hacker News). I don't know Haskell. Could someone explain—what does the code do, how do the algorithms work, why is the second faster?


@matt-hickford: with fold you apply buf_append on every element (=n times) of strs with accumulating the result in each step. fold is O(n), buff_append (with a list like implementation) is O(n). so, together O(nO(n)) = O(nn) = O(n^2) (or: O(n*m))

buf_concat (glue list of lists together) runs in O(n). reverse runs in O(n), but isn't applied on every element. So, together in O(n) + O(n) = O(n+n) = O(n)


Why is the 'reverse' needed, at all? A 'reverse' without the actual need to reverse a list often indicates that someone has assembled the data in the wrong order before.


A great explanation of this was featured on Accidentally Quadratic

Please sign in to comment.
Something went wrong with that request. Please try again.