Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

A way to measure performance

Tags:

haskell

Given Exercise 14 from 99 Haskell Problems:

(*) Duplicate the elements of a list.

Eg.:

*Main> dupli''' [1..10]
[1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10]

I've implemented 4 solutions:

{-- my first attempt --}
dupli :: [a] -> [a]
dupli [] = []
dupli (x:xs) = replicate 2 x ++ dupli xs

{-- using concatMap and replicate --}
dupli' :: [a] -> [a]
dupli' xs = concatMap (replicate 2) xs

{-- usign foldl --}
dupli'' :: [a] -> [a]
dupli'' xs = foldl (\acc x -> acc ++ [x,x]) [] xs

{-- using foldl 2 --}
dupli''' :: [a] -> [a]
dupli''' xs = reverse $ foldl (\acc x -> x:x:acc) [] xs

Still, I don't know how to really measure performance .

So what's the recommended function (from the above list) in terms of performance .

Any suggestions ?

like image 868
Andrei Ciobanu Avatar asked Dec 10 '22 11:12

Andrei Ciobanu


1 Answers

These all seem more complicated (and/or less efficient) than they need to be. Why not just this:

dupli [] = []
dupli (x:xs) = x:x:(dupli xs)

Your last example is close to a good fold-based implementation, but you should use foldr, which will obviate the need to reverse the result:

dupli = foldr (\x xs -> x:x:xs) []

As for measuring performance, the "empirical approach" is profiling. As Haskell programs grow in size, they can get fairly hard to reason about in terms of runtime and space complexity, and profiling is your best bet. Also, a crude but often effective empirical approach when gauging the relative complexity of two functions is to simply compare how long they each take on some sufficiently large input; e.g. time how long length $ dupli [1..1000000] takes and compare it to dupli'', etc.

But for a program this small it shouldn't be too hard to figure out the runtime complexity of the algorithm based on your knowledge of the data structure(s) in question--in this case, lists. Here's a tip: any time you use concatenation (x ++ y), the runtime complexity is O(length x). If concatenation is used inside of a recursive algorithm operating on all the elements of a list of size n, you will essentially have an O(n ^2) algorithm. Both examples I gave, and your last example, are O(n), because the only operation used inside the recursive definition is (:), which is O(1).

like image 88
Tom Crockett Avatar answered Dec 25 '22 07:12

Tom Crockett