[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.
--
Chris
More information about the Haskell-Cafe
mailing list