| 1 | {-# LANGUAGE NoMonomorphismRestriction, ScopedTypeVariables, ForeignFunctionInterface #-} |
|---|
| 2 | {-# OPTIONS -#include "WCsubst.h" #-} |
|---|
| 3 | |
|---|
| 4 | import Foreign.C.Types -- (CInt) |
|---|
| 5 | import System.IO.Unsafe (unsafePerformIO) |
|---|
| 6 | import Criterion.Main (defaultMain, bgroup, bench) |
|---|
| 7 | import System.IO |
|---|
| 8 | import System.Random |
|---|
| 9 | import qualified Data.List |
|---|
| 10 | import Data.Char (ord) |
|---|
| 11 | import Test.QuickCheck |
|---|
| 12 | |
|---|
| 13 | |
|---|
| 14 | main :: IO () |
|---|
| 15 | main = do grade <- test |
|---|
| 16 | case grade of |
|---|
| 17 | Success _ -> defaultMain [ |
|---|
| 18 | bgroup "random" [-- bench "yhc" $ runOnce yspace randomChars, |
|---|
| 19 | bench "ghc" $ runOnce gspace randomChars, |
|---|
| 20 | bench "ghc'" $ runOnce gspace' randomChars, |
|---|
| 21 | bench "ghc''" $ runOnce gspace'' randomChars, |
|---|
| 22 | bench "ghc'''" $ runOnce gspace''' randomChars, |
|---|
| 23 | bench "ffi'" $ runOnce ffispace' randomChars, |
|---|
| 24 | bench "ffi" $ runOnce ffispace randomChars], |
|---|
| 25 | bgroup "shakespeare" [-- bench "yhc" $ runOnce yspace shakespeare, |
|---|
| 26 | bench "ghc-dryrun" $ runOnce gspace shakespeare, |
|---|
| 27 | bench "ghc-dryrun" $ runOnce gspace shakespeare, |
|---|
| 28 | bench "ghc" $ runOnce gspace shakespeare, |
|---|
| 29 | bench "ghc'" $ runOnce gspace' shakespeare, |
|---|
| 30 | bench "ghc''" $ runOnce gspace'' shakespeare, |
|---|
| 31 | bench "ghc'''" $ runOnce gspace''' shakespeare, |
|---|
| 32 | bench "ffi'" $ runOnce ffispace' shakespeare], |
|---|
| 33 | bgroup "shakespeare" [-- bench "yhc" $ runOnce yspace allChars, |
|---|
| 34 | bench "ghc" $ runOnce gspace allChars, |
|---|
| 35 | bench "ghc'" $ runOnce gspace' allChars, |
|---|
| 36 | bench "ghc''" $ runOnce gspace'' allChars, |
|---|
| 37 | bench "ghc'''" $ runOnce gspace''' allChars, |
|---|
| 38 | bench "ffi'" $ runOnce ffispace' allChars, |
|---|
| 39 | bench "ffi" $ runOnce ffispace allChars], |
|---|
| 40 | bgroup "space" [-- bench "yhc" $ runOnce yspace space, |
|---|
| 41 | bench "ghc" $ runOnce gspace space, |
|---|
| 42 | bench "ghc'" $ runOnce gspace' space, |
|---|
| 43 | bench "ghc''" $ runOnce gspace'' space, |
|---|
| 44 | bench "ghc'''" $ runOnce gspace''' space, |
|---|
| 45 | bench "ffi'" $ runOnce ffispace' space, |
|---|
| 46 | bench "ffi" $ runOnce ffispace space], |
|---|
| 47 | bgroup "b" [-- bench "yhc" $ runOnce yspace b, |
|---|
| 48 | bench "ghc" $ runOnce gspace b, |
|---|
| 49 | bench "ghc'" $ runOnce gspace' b, |
|---|
| 50 | bench "ghc''" $ runOnce gspace'' b, |
|---|
| 51 | bench "ghc'''" $ runOnce gspace''' b, |
|---|
| 52 | bench "ffi'" $ runOnce ffispace' b, |
|---|
| 53 | bench "ffi" $ runOnce ffispace b], |
|---|
| 54 | bgroup "t" [-- bench "yhc" $ runOnce yspace t, |
|---|
| 55 | bench "ghc" $ runOnce gspace t, |
|---|
| 56 | bench "ghc'" $ runOnce gspace' t, |
|---|
| 57 | bench "ghc''" $ runOnce gspace'' t, |
|---|
| 58 | bench "ghc'''" $ runOnce gspace''' t, |
|---|
| 59 | bench "ffi'" $ runOnce ffispace' t, |
|---|
| 60 | bench "ffi" $ runOnce ffispace t], |
|---|
| 61 | bgroup "n" [-- bench "yhc" $ runOnce yspace n, |
|---|
| 62 | bench "ghc" $ runOnce gspace n, |
|---|
| 63 | bench "ghc'" $ runOnce gspace' n, |
|---|
| 64 | bench "ghc''" $ runOnce gspace'' n, |
|---|
| 65 | bench "ghc'''" $ runOnce gspace''' n, |
|---|
| 66 | bench "ffi'" $ runOnce ffispace' n, |
|---|
| 67 | bench "ffi" $ runOnce ffispace n], |
|---|
| 68 | bgroup "r" [-- bench "yhc" $ runOnce yspace r, |
|---|
| 69 | bench "ghc" $ runOnce gspace r, |
|---|
| 70 | bench "ghc'" $ runOnce gspace' r, |
|---|
| 71 | bench "ghc''" $ runOnce gspace'' r, |
|---|
| 72 | bench "ghc'''" $ runOnce gspace''' r, |
|---|
| 73 | bench "ffi'" $ runOnce ffispace' r, |
|---|
| 74 | bench "ffi" $ runOnce ffispace r], |
|---|
| 75 | bgroup "f" [-- bench "yhc" $ runOnce yspace f, |
|---|
| 76 | bench "ghc" $ runOnce gspace f, |
|---|
| 77 | bench "ghc'" $ runOnce gspace' f, |
|---|
| 78 | bench "ghc''" $ runOnce gspace'' f, |
|---|
| 79 | bench "ghc'''" $ runOnce gspace''' f, |
|---|
| 80 | bench "ffi'" $ runOnce ffispace' f, |
|---|
| 81 | bench "ffi" $ runOnce ffispace f], |
|---|
| 82 | bgroup "v" [-- bench "yhc" $ runOnce yspace v, |
|---|
| 83 | bench "ghc" $ runOnce gspace v, |
|---|
| 84 | bench "ghc'" $ runOnce gspace' v, |
|---|
| 85 | bench "ghc''" $ runOnce gspace'' v, |
|---|
| 86 | bench "ghc'''" $ runOnce gspace''' v, |
|---|
| 87 | bench "ffi'" $ runOnce ffispace' v, |
|---|
| 88 | bench "ffi" $ runOnce ffispace v], |
|---|
| 89 | bgroup "xa0" [-- bench "yhc" $ runOnce yspace xa0, |
|---|
| 90 | bench "ghc" $ runOnce gspace xa0, |
|---|
| 91 | bench "ghc'" $ runOnce gspace' xa0, |
|---|
| 92 | bench "ghc''" $ runOnce gspace'' xa0, |
|---|
| 93 | bench "ghc'''" $ runOnce gspace''' xa0, |
|---|
| 94 | bench "ffi'" $ runOnce ffispace' xa0, |
|---|
| 95 | bench "ffi" $ runOnce ffispace xa0], |
|---|
| 96 | bgroup "xff" [-- bench "yhc" $ runOnce yspace xff, |
|---|
| 97 | bench "ghc" $ runOnce gspace xff, |
|---|
| 98 | bench "ghc'" $ runOnce gspace' xff, |
|---|
| 99 | bench "ghc''" $ runOnce gspace'' xff, |
|---|
| 100 | bench "ghc'''" $ runOnce gspace''' xff, |
|---|
| 101 | bench "ffi'" $ runOnce ffispace' xff, |
|---|
| 102 | bench "ffi" $ runOnce ffispace xff] |
|---|
| 103 | ] |
|---|
| 104 | _ -> error "isSpace optimizations no longer correct?!" |
|---|
| 105 | |
|---|
| 106 | runOnce :: (Char -> Bool) -> (Int -> [Char]) -> IO () |
|---|
| 107 | {- NOINLINE runOnce #-} |
|---|
| 108 | runOnce testspace generator = let g = generator (13^(24::Int)) in |
|---|
| 109 | let testslist = map testspace g in |
|---|
| 110 | let slist = map gspace g in |
|---|
| 111 | if slist == testslist then putStr "" else print g |
|---|
| 112 | |
|---|
| 113 | -- QuickCheck |
|---|
| 114 | test :: IO Result |
|---|
| 115 | test = quickCheckWithResult stdArgs{maxSuccess = 10^(4::Int), maxSize = 10^(22::Int)} space_props |
|---|
| 116 | |
|---|
| 117 | -- ffispace and ffispace' omitted because without checks, they aren't the same |
|---|
| 118 | -- as the Haskell functions |
|---|
| 119 | space_props :: Char -> Bool |
|---|
| 120 | space_props x = all (== gspace x) [yspace x, gspace' x, gspace'' x, gspace''' x] |
|---|
| 121 | |
|---|
| 122 | -- Generators |
|---|
| 123 | randomChars :: Int -> String |
|---|
| 124 | randomChars x = take x $ randoms $ unsafePerformIO newStdGen |
|---|
| 125 | |
|---|
| 126 | allChars :: Int -> String |
|---|
| 127 | allChars x = take x ['a'..] |
|---|
| 128 | |
|---|
| 129 | {- NOINLINE shakespeare #-} |
|---|
| 130 | shakespeare :: b -> String |
|---|
| 131 | shakespeare = const $ unsafePerformIO $ readFile "shaks12.txt" |
|---|
| 132 | |
|---|
| 133 | space,b,t,n,r,f,v,xa0,xff :: Int -> String |
|---|
| 134 | space = flip replicate ' ' |
|---|
| 135 | b = flip replicate '\b' |
|---|
| 136 | t = flip replicate '\t' |
|---|
| 137 | n = flip replicate '\n' |
|---|
| 138 | r = flip replicate '\r' |
|---|
| 139 | f = flip replicate '\f' |
|---|
| 140 | v = flip replicate '\v' |
|---|
| 141 | xa0 = flip replicate '\xa0' |
|---|
| 142 | xff = flip replicate '\xff' |
|---|
| 143 | |
|---|
| 144 | -- Implementations |
|---|
| 145 | |
|---|
| 146 | -- YHC |
|---|
| 147 | yspace, gspace, gspace', gspace'', gspace''', ffispace, ffispace' :: Char -> Bool |
|---|
| 148 | yspace c = c `elem` " \t\n\r\f\v\xa0" |
|---|
| 149 | |
|---|
| 150 | -- GHC |
|---|
| 151 | |
|---|
| 152 | -- | Selects white-space characters in the Latin-1 range. |
|---|
| 153 | -- (In Unicode terms, this includes spaces and some control characters.) |
|---|
| 154 | -- isSpace includes non-breaking space |
|---|
| 155 | -- Done with explicit equalities both for efficiency, and to avoid a tiresome |
|---|
| 156 | -- recursion with GHC.List elem |
|---|
| 157 | gspace c = c == ' ' || |
|---|
| 158 | c == '\t' || |
|---|
| 159 | c == '\n' || |
|---|
| 160 | c == '\r' || |
|---|
| 161 | c == '\f' || |
|---|
| 162 | c == '\v' || |
|---|
| 163 | c == '\xa0' || |
|---|
| 164 | iswspace (fromIntegral (ord c)) /= 0 |
|---|
| 165 | |
|---|
| 166 | foreign import ccall unsafe "u_iswspace" |
|---|
| 167 | iswspace :: CInt -> CInt |
|---|
| 168 | |
|---|
| 169 | -- first GHC variation: avoid iswspace call |
|---|
| 170 | -- based on ndm's suggestion |
|---|
| 171 | -- <http://www.haskell.org/pipermail/glasgow-haskell-users/2007-May/012612.html> |
|---|
| 172 | gspace' c |
|---|
| 173 | | charvalue < 255 = c == ' ' || |
|---|
| 174 | c == '\t' || |
|---|
| 175 | c == '\n' || |
|---|
| 176 | c == '\r' || |
|---|
| 177 | c == '\f' || |
|---|
| 178 | c == '\v' || |
|---|
| 179 | c == '\xa0' |
|---|
| 180 | | otherwise = iswspace charvalue /= 0 |
|---|
| 181 | where charvalue :: CInt |
|---|
| 182 | charvalue = fromIntegral (ord c) |
|---|
| 183 | |
|---|
| 184 | |
|---|
| 185 | -- Yitzchak Gale's version: |
|---|
| 186 | -- <http://www.haskell.org/pipermail/glasgow-haskell-users/2007-May/012616.html> |
|---|
| 187 | gspace'' c = c == ' ' |
|---|
| 188 | || c <= '\r' && c >= '\t' |
|---|
| 189 | || c == '\xa0' |
|---|
| 190 | || c > '\xff' && iswspace (fromIntegral (ord c)) /= 0 |
|---|
| 191 | |
|---|
| 192 | -- Ketil Malde's version: |
|---|
| 193 | -- <http://www.haskell.org/pipermail/glasgow-haskell-users/2007-May/012618.html> |
|---|
| 194 | -- slightly modified for correctness |
|---|
| 195 | gspace''' = isSp . ord |
|---|
| 196 | where isSp :: Int -> Bool |
|---|
| 197 | isSp c | c <= 13 = c > 8 -- \b..\r |
|---|
| 198 | | c <= 127 = c == 32 -- ' ' |
|---|
| 199 | | c <= 255 = c == 0xa0 -- nbsp |
|---|
| 200 | | otherwise = iswspace (fromIntegral c) /= 0 |
|---|
| 201 | |
|---|
| 202 | -- not a correct 'isSpace' implementation (eg '\n'), but we include this to get an idea of how slow 'iswspace' is |
|---|
| 203 | ffispace c = iswspace (fromIntegral (ord c)) /= 0 |
|---|
| 204 | |
|---|
| 205 | ffispace' c = isspace (fromIntegral (ord c)) /= 0 |
|---|
| 206 | |
|---|
| 207 | foreign import ccall unsafe "isspace" |
|---|
| 208 | isspace :: CInt -> CInt |
|---|