[Haskell-cafe] Progress on shootout entries

Chris Kuklewicz haskell at list.mightyreason.com
Wed Jan 4 08:11:34 EST 2006


Krasimir Angelov wrote:
> 2006/1/3, Chris Kuklewicz <haskell at list.mightyreason.com>:
> 
>> And finially, the haskel entry for
>>http://shootout.alioth.debian.org/benchmark.php?test=fannkuch&lang=all
>> is currently the *slowest* entry out of 28 languages.  It is 813x
>>slower than the c-code, 500x slower than OCaml.  Should be easy to make
>>it faster...
> 
> 
> In this particular case the flop function is very slow.
> 
> flop :: Int8 -> [Int8] -> Int8
> flop acc (1:xs) = acc
> flop acc list@(x:xs) = flop (acc+1) mangle
>     where   mangle = (reverse front) ++ back
>             (front,back) = splitAt (fromIntegral x) list
> 
> 
> It can be optimized using a new mangle function:
> 
> mangle :: Int -> [a] -> [a]
> mangle m xs = xs'
>   where
>     (rs,xs') = splitAt m xs rs
> 
>     splitAt :: Int -> [a] -> [a] -> ([a], [a])
>     splitAt 0    xs  ys = (xs,ys)
>     splitAt _    []  ys = ([],ys)
>     splitAt m (x:xs) ys = splitAt (m - 1) xs (x:ys)
> 
> The mangle function transforms the list in one step while the original
> implementation is using reverse, (++) and splitAt. With this function
> the new flop is:
> 
> flop :: Int8 -> [Int8] -> Int8
> flop acc (1:xs) = acc
> flop acc list@(x:xs) = flop (acc+1) (mangle (fromIntegral x) list)

You seem to have also discovered the fast way to flop.

This benchmarks exactly as fast as the similar entry assembled by
Bertram Felgenhauer using Jan-Willem Maessen's flop code:

> import System (getArgs)
> import Data.List (foldl', tails)
> 
> rotate n (x:xs) = rot' n xs where
>     rot' 1 xs     = x:xs
>     rot' n (x:xs) = x:rot' (n-1) xs
> 
> permutations l = foldr perm' [l] [2..length l] where
>     perm' n l = l >>= take n . iterate (rotate n)
> 
> flop :: Int -> [Int] -> [Int]
> flop n xs = rs
>   where (rs, ys) = fl n xs ys
>         fl 0 xs     ys = (ys, xs)
>         fl n (x:xs) ys = fl (n-1) xs (x:ys)
> 
> steps :: Int -> [Int] -> Int
> steps n (1:_)    = n
> steps n ts@(t:_) = (steps $! (n+1)) (flop t ts)
> 
> main = do
>     args <- getArgs
>     let arg = if null args then 7 else read $ head args
>     mapM_ (putStrLn . concatMap show) $ take 30 $ permutations [1..arg]
>     putStr $ "Pfannkuchen(" ++ show arg ++ ") = "
>     putStrLn $ show $ foldl' (flip (max . steps 0)) 0 $ permutations [1..arg]

[ This is on the wiki, and is 80-90 times faster than the old entry ]

I have not been able to make this run any faster by tweaking it.  It is
easily one of the nicest lazy Haskell-idiom entries on the whole
shootout.  It does not have to use IO or ST or unboxed anything or even
arrays to perform well in small space.

* Replacing the foldl' with the more legible foldl' max 0 $ map (steps
0) is a very very tiny speed loss
* Going to Word8 instead of Int does not improve speed or save space
* Using Control.Monad.fix explicitly is speed neutral:
> flopF :: Int -> [Int] -> [Int]
> flopF n xs = fst $ fix (flop' n xs) where
>  -- flop' :: Int -> [Int] -> ([Int],[Int]) -> ([Int],[Int])
>     flop' 0 xs     ~(_,ys) = (ys,xs)
>     flop' n (x:xs) ~(rs,ys) = flop' (n-1) xs (rs,(x:ys))

-- 
Chris


More information about the Haskell-Cafe mailing list