[Haskell-cafe] Shootout favoring imperative code
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.
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