[Haskell-cafe] Shootout favoring imperative code

Chris Kuklewicz haskell at list.mightyreason.com
Fri Jan 6 04:51:47 EST 2006

Relative speed comparison below

Udo Stenzel wrote:
> Sebastian Sylvan wrote:
>>On 1/5/06, Chris Kuklewicz <haskell at list.mightyreason.com> wrote:
>>>There is no need to beat a dead horse, though.  This benchmark sets out
>>>to test fgets / atoi, and that is all.  There are better benchmarks to
>>>spend time on.
>>I agree. The benchmark really is about how fast you can call low-level
>>IO system calls. But since Haskell is a high-level language it's
>>natural that it's a bit difficult to get access to these unsafe (but
>>fast) low-level functions.
> There's probably a bit more to it.  First off, one could legitimately
> argue that (liftM lines getContents) is the Haskell way to do line
> oriented IO.  The real question is, why does the fast solution have to
> be ugly
>>foreign import ccall "stdio.h" fgets :: CString -> Int -> Ptr CFile ->IO CString
>>foreign import ccall safe "stdlib.h" atoi :: CString -> Int
>>foreign import ccall safe "stdio.h &(*stdin)" c_stdin :: Ptr CFile
> and why does the idiomatic solution have to be slow?
> | main = print . sum . map read . lines =<< getContents
> The biggest hit is probably the construction of a huge String as linked
> list, which cannot be deforested away (not with the foldr/build
> mechanism anyway).  Assuming we find a better representation for
> Strings, we could make some headway here and in many other benchmarks.
> So I think, just by replacing String and along with it getContents,
> lines and read, we will get competitive speed and retain the ability to
> handle arbitrarily long lines.  Of course, the shootout wouldn't care
> for a feature that is otherwise quite important in practice...  Anyway,
> I'll try try to come up with something and then follow up on this.
> Udo.

The solution on the wiki (Char by Char) is the fastest I could make :
>   print . total =<< getContents
Time was 1.00 seconds

I tried using getLine and it was slower:
>   let next rt = do line <- catch getLine (\_ -> return [])
>                    if (null line) then return (I# rt)
>                                   else next (rt +# aLine line)
Time was 3.79 seconds

I tried using getContents and lines and it was slowest:
>   total <- return.(next 0#).lines =<< getContents
Time was 4.76 seconds

>From this, I assume the buffering that getContents does is very
optimized.  The cost of getLine was very nontrivial.  So for non-binary
input, I would recommend using getContents and processing it Char by Char.


More information about the Haskell-Cafe mailing list