├── .gitignore ├── stack.yaml ├── Common.hs ├── stack.yaml.lock ├── LICENSE ├── bench.cabal ├── .travis.yml ├── Space.hs ├── Report.hs ├── README.md └── Time.hs /.gitignore: -------------------------------------------------------------------------------- 1 | .stack-work 2 | -------------------------------------------------------------------------------- /stack.yaml: -------------------------------------------------------------------------------- 1 | resolver: lts-16.31 2 | extra-deps: 3 | - judy-0.4.1 4 | - bytestring-trie-0.2.6 5 | -------------------------------------------------------------------------------- /Common.hs: -------------------------------------------------------------------------------- 1 | {-# OPTIONS_GHC -fno-warn-orphans #-} 2 | module Common where 3 | 4 | import Control.DeepSeq 5 | import qualified Data.HashTable.ST.Basic 6 | import qualified Data.HashTable.ST.Cuckoo 7 | import qualified Data.HashTable.ST.Linear 8 | 9 | 10 | instance NFData (Data.HashTable.ST.Basic.HashTable s k v) where 11 | rnf x = seq x () 12 | 13 | instance NFData (Data.HashTable.ST.Cuckoo.HashTable s k v) where 14 | rnf x = seq x () 15 | 16 | instance NFData (Data.HashTable.ST.Linear.HashTable s k v) where 17 | rnf x = seq x () 18 | -------------------------------------------------------------------------------- /stack.yaml.lock: -------------------------------------------------------------------------------- 1 | # This file was autogenerated by Stack. 2 | # You should not edit this file by hand. 3 | # For more information, please see the documentation at: 4 | # https://docs.haskellstack.org/en/stable/lock_files 5 | 6 | packages: 7 | - completed: 8 | hackage: judy-0.4.1@sha256:cde65e25880b2833e3105f86a79361f1bc37052eb3fe9d8795ab7bbd35da5cc0,1394 9 | pantry-tree: 10 | size: 441 11 | sha256: b6646f406ef1f1335dac3da4e8864b214262a53db0c03f107c65287b101345f4 12 | original: 13 | hackage: judy-0.4.1 14 | - completed: 15 | hackage: bytestring-trie-0.2.6@sha256:ba9907193f2ef0c2bd3d83dbf5a10182d456acb6127d33cc9931d6b55b1a3e0e,4698 16 | pantry-tree: 17 | size: 814 18 | sha256: bab255b3ec340550f7d4fc1e1ef6a7bc61768288cfdd4620e76640d0b6bc0472 19 | original: 20 | hackage: bytestring-trie-0.2.6 21 | snapshots: 22 | - completed: 23 | size: 534126 24 | url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/16/31.yaml 25 | sha256: 637fb77049b25560622a224845b7acfe81a09fdb6a96a3c75997a10b651667f6 26 | original: lts-16.31 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Chris Done 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /bench.cabal: -------------------------------------------------------------------------------- 1 | name: bench 2 | version: 0 3 | build-type: Simple 4 | cabal-version: >=1.10 5 | 6 | library 7 | build-depends: base 8 | default-language: Haskell2010 9 | 10 | test-suite space 11 | default-language: Haskell2010 12 | type: exitcode-stdio-1.0 13 | ghc-options: -O2 14 | main-is: Space.hs 15 | other-modules: Common 16 | build-depends: base 17 | , weigh 18 | , deepseq 19 | , containers 20 | , unordered-containers 21 | , bytestring-trie 22 | , bytestring 23 | , random 24 | , hashtables 25 | 26 | benchmark time 27 | default-language: Haskell2010 28 | type: exitcode-stdio-1.0 29 | ghc-options: -Wall -O2 -rtsopts 30 | main-is: Time.hs 31 | other-modules: Common 32 | build-depends: base 33 | , bytestring 34 | , ghc-prim 35 | , criterion 36 | , deepseq 37 | , containers 38 | , unordered-containers 39 | , bytestring-trie 40 | , random, directory 41 | , hashtables 42 | 43 | executable report 44 | default-language: Haskell2010 45 | ghc-options: -Wall -O2 -rtsopts 46 | main-is: Report.hs 47 | build-depends: base 48 | , bytestring, directory 49 | , vector 50 | , ghc-prim 51 | , criterion 52 | , deepseq 53 | , containers 54 | , csv 55 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: c 3 | cache: 4 | directories: 5 | - "$HOME/.ghc" 6 | - "$HOME/.cabal" 7 | - "$HOME/.stack" 8 | matrix: 9 | include: 10 | - env: BUILD=stack ARGS="--resolver lts-6.20" 11 | compiler: ": #stack 7.10.3" 12 | addons: 13 | apt: 14 | packages: 15 | - libgmp-dev 16 | - libjudydebian1 17 | - libjudy-dev 18 | 19 | before_install: 20 | # Using compiler above sets CC to an invalid value, so unset it 21 | - unset CC 22 | 23 | # We want to always allow newer versions of packages when building on GHC HEAD 24 | - CABALARGS="" 25 | - if [ "x$GHCVER" = "xhead" ]; then CABALARGS=--allow-newer; fi 26 | 27 | # Download and unpack the stack executable 28 | - export PATH=/opt/ghc/$GHCVER/bin:/opt/cabal/$CABALVER/bin:$HOME/.local/bin:/opt/alex/$ALEXVER/bin:/opt/happy/$HAPPYVER/bin:$HOME/.cabal/bin:$PATH 29 | - mkdir -p ~/.local/bin 30 | - | 31 | if [ `uname` = "Darwin" ] 32 | then 33 | travis_retry curl --insecure -L https://www.stackage.org/stack/osx-x86_64 | tar xz --strip-components=1 --include '*/stack' -C ~/.local/bin 34 | else 35 | travis_retry curl -L https://www.stackage.org/stack/linux-x86_64 | tar xz --wildcards --strip-components=1 -C ~/.local/bin '*/stack' 36 | fi 37 | 38 | # Use the more reliable S3 mirror of Hackage 39 | mkdir -p $HOME/.cabal 40 | echo 'remote-repo: hackage.haskell.org:http://hackage.fpcomplete.com/' > $HOME/.cabal/config 41 | echo 'remote-repo-cache: $HOME/.cabal/packages' >> $HOME/.cabal/config 42 | 43 | if [ "$CABALVER" != "1.16" ] 44 | then 45 | echo 'jobs: $ncpus' >> $HOME/.cabal/config 46 | fi 47 | 48 | install: 49 | - echo "$(ghc --version) [$(ghc --print-project-git-commit-id 2> /dev/null || echo '?')]" 50 | - if [ -f configure.ac ]; then autoreconf -i; fi 51 | - | 52 | set -ex 53 | case "$BUILD" in 54 | stack) 55 | stack --no-terminal --install-ghc $ARGS test --bench --only-dependencies 56 | ;; 57 | cabal) 58 | cabal --version 59 | travis_retry cabal update 60 | 61 | # Get the list of packages from the stack.yaml file 62 | PACKAGES=$(stack --install-ghc query locals | grep '^ *path' | sed 's@^ *path:@@') 63 | 64 | cabal install --only-dependencies --enable-tests --enable-benchmarks --force-reinstalls --ghc-options=-O0 --reorder-goals --max-backjumps=-1 $CABALARGS $PACKAGES 65 | ;; 66 | esac 67 | set +ex 68 | 69 | script: 70 | - | 71 | set -ex 72 | case "$BUILD" in 73 | stack) 74 | stack --no-terminal $ARGS test --bench --no-run-benchmarks 75 | ;; 76 | cabal) 77 | cabal install --enable-tests $RUN_TESTS --enable-benchmarks --force-reinstalls --ghc-options=-O0 --reorder-goals --max-backjumps=-1 $CABALARGS $PACKAGES 78 | 79 | ORIGDIR=$(pwd) 80 | for dir in $PACKAGES 81 | do 82 | cd $dir 83 | cabal check || [ "$CABALVER" == "1.16" ] 84 | cabal sdist 85 | PKGVER=$(cabal info . | awk '{print $2;exit}') 86 | SRC_TGZ=$PKGVER.tar.gz 87 | cd dist 88 | tar zxfv "$SRC_TGZ" 89 | cd "$PKGVER" 90 | cabal configure --enable-tests 91 | cabal build 92 | cd $ORIGDIR 93 | done 94 | ;; 95 | esac 96 | set +ex 97 | -------------------------------------------------------------------------------- /Space.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE BangPatterns #-} 2 | {-# LANGUAGE DeriveGeneric #-} 3 | 4 | -- | Example uses of comparing map-like data structures. 5 | 6 | module Main where 7 | 8 | import Common 9 | import Control.Arrow 10 | import Control.DeepSeq 11 | import qualified Data.ByteString as S 12 | import qualified Data.ByteString.Char8 as S8 13 | import qualified Data.HashMap.Lazy 14 | import qualified Data.HashMap.Strict 15 | import qualified Data.HashTable.IO 16 | import qualified Data.IntMap.Lazy 17 | import qualified Data.IntMap.Strict 18 | import qualified Data.Map.Lazy 19 | import qualified Data.Map.Strict 20 | import qualified Data.Trie 21 | import System.Random 22 | import Weigh 23 | 24 | -- | Weigh maps. 25 | main :: IO () 26 | main = 27 | mainWith 28 | (do setColumns [Case,Allocated,Max,Live,GCs] 29 | inserts 30 | fromlists 31 | frombytestrings) 32 | 33 | inserts :: Weigh () 34 | inserts = do func "Data.Map.Strict.insert mempty" 35 | (\(k,v) -> Data.Map.Strict.insert k v mempty) 36 | (1 :: Int,1 :: Int) 37 | func "Data.Map.Lazy.insert mempty" 38 | (\(k,v) -> Data.Map.Lazy.insert k v mempty) 39 | (1 :: Int,1 :: Int) 40 | func "Data.HashMap.Strict.insert mempty" 41 | (\(k,v) -> Data.HashMap.Strict.insert k v mempty) 42 | (1 :: Int,1 :: Int) 43 | func "Data.HashMap.Lazy.insert mempty" 44 | (\(k,v) -> Data.HashMap.Lazy.insert k v mempty) 45 | (1 :: Int,1 :: Int) 46 | 47 | fromlists :: Weigh () 48 | fromlists = 49 | do let !elems = 50 | force (zip (randoms (mkStdGen 0) :: [Int]) 51 | [1 :: Int .. 1000000]) 52 | func "Data.Map.Strict.fromList (1 million)" Data.Map.Strict.fromList elems 53 | func "Data.Map.Lazy.fromList (1 million)" Data.Map.Lazy.fromList elems 54 | func "Data.IntMap.Strict.fromList (1 million)" Data.IntMap.Strict.fromList elems 55 | func "Data.IntMap.Lazy.fromList (1 million)" Data.IntMap.Lazy.fromList elems 56 | func "Data.HashMap.Strict.fromList (1 million)" Data.HashMap.Strict.fromList elems 57 | func "Data.HashMap.Lazy.fromList (1 million)" Data.HashMap.Lazy.fromList elems 58 | io "Data.HashTable.IO.BasicHashTable (1 million)" 59 | (Data.HashTable.IO.fromList :: [(Int,Int)] -> IO (Data.HashTable.IO.BasicHashTable Int Int)) 60 | elems 61 | io "Data.HashTable.IO.CuckooHashTable (1 million)" 62 | (Data.HashTable.IO.fromList :: [(Int,Int)] -> IO (Data.HashTable.IO.CuckooHashTable Int Int)) 63 | elems 64 | io "Data.HashTable.IO.LinearHashTable (1 million)" 65 | (Data.HashTable.IO.fromList :: [(Int,Int)] -> IO (Data.HashTable.IO.LinearHashTable Int Int)) 66 | elems 67 | 68 | 69 | frombytestrings :: Weigh () 70 | frombytestrings = 71 | do let !elems = 72 | force 73 | (map 74 | (first (S8.pack . show)) 75 | (take 1000000 (zip (randoms (mkStdGen 0) :: [Int]) [1 :: Int ..]))) 76 | func "Data.Map.Strict.fromList (1 million BS)" Data.Map.Strict.fromList elems 77 | func "Data.Map.Lazy.fromList (1 million BS)" Data.Map.Lazy.fromList elems 78 | func "Data.HashMap.Strict.fromList (1 million BS)" Data.HashMap.Strict.fromList elems 79 | func "Data.HashMap.Lazy.fromList (1 million BS)" Data.HashMap.Lazy.fromList elems 80 | func "Data.Trie.fromList (1 million BS)" Data.Trie.fromList elems 81 | 82 | instance NFData (Data.Trie.Trie a) where 83 | rnf x = seq x () 84 | -------------------------------------------------------------------------------- /Report.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE ScopedTypeVariables #-} 2 | {-# OPTIONS_GHC -fno-warn-name-shadowing #-} 3 | {-# LANGUAGE ExistentialQuantification #-} 4 | {-# LANGUAGE BangPatterns #-} 5 | 6 | module Main (main) where 7 | 8 | import Control.DeepSeq 9 | import Data.Function 10 | import Data.List 11 | import System.Environment 12 | import Text.CSV 13 | import Text.Printf 14 | 15 | main :: IO () 16 | main = do 17 | fp:_ <- getArgs 18 | reportFromCsv fp 19 | 20 | reportFromCsv :: FilePath -> IO () 21 | reportFromCsv fp = do 22 | result <- parseCSVFromFile fp 23 | case result of 24 | Right (_:rows) -> do 25 | !readme <- fmap force (readFile "README.md") 26 | let sep = "" 27 | before = unlines (takeWhile (/= sep) (lines readme) ++ [sep ++ "\n"]) 28 | writeFile 29 | "README.md" 30 | (before ++ 31 | unlines 32 | (map 33 | format 34 | (filter 35 | (not . null . filter (not . null . filter (not . null))) 36 | (groupBy (on (==) (takeWhile (/= '/') . concat . take 1)) rows)))) 37 | _ -> error "Couldn't parse csv" 38 | 39 | format :: [[String]] -> String 40 | format rows = 41 | ("## " ++ takeWhile (/= '/') (concat (concat (take 1 (drop 1 rows))))) ++ 42 | "\n\n" ++ 43 | unlines 44 | [ ("|Name|" ++ intercalate "|" scales ++ "|") 45 | , "|" ++ concat (replicate (1 + length scales) "---|") 46 | ] ++ 47 | unlines 48 | (map 49 | (\name -> 50 | "|" ++ name ++ "|" ++ intercalate "|" (valuesByName name) ++ "|") 51 | (names)) 52 | where 53 | valuesByName name = 54 | map 55 | (\row@(_:mean:_) -> 56 | let scale = rowScale row 57 | in float (valuesByScale scale) (read mean)) 58 | (filter ((== name) . rowName) rows) 59 | valuesByScale scale = 60 | map (\(_:mean:_) -> read mean) (filter ((== scale) . rowScale) rows) 61 | names = nub (map rowName rows) 62 | scales = nub (map rowScale rows) 63 | rowName row = 64 | let s = 65 | takeWhile 66 | (/= ':') 67 | (dropWhile (== '/') (dropWhile (/= '/') (concat (take 1 row)))) 68 | in s 69 | rowScale row = 70 | let scale = dropWhile (== ':') (dropWhile (/= ':') (concat (take 1 row))) 71 | in scale 72 | 73 | float :: [Double] -> Double -> String 74 | float others x = let (scale, ext) = secs (mean others) 75 | in with (x * scale) ext 76 | 77 | -- | Convert a number of seconds to a string. The string will consist 78 | -- of four decimal places, followed by a short description of the time 79 | -- units. 80 | secs :: Double -> (Double, String) 81 | secs k 82 | | k >= 1 = 1 `pair` "s" 83 | | k >= 1e-3 = 1e3 `pair` "ms" 84 | | k >= 1e-6 = 1e6 `pair` "μs" 85 | | k >= 1e-9 = 1e9 `pair` "ns" 86 | | k >= 1e-12 = 1e12 `pair` "ps" 87 | | k >= 1e-15 = 1e15 `pair` "fs" 88 | | k >= 1e-18 = 1e18 `pair` "as" 89 | | otherwise = error "Bad scale" 90 | where pair= (,) 91 | 92 | with :: Double -> String -> String 93 | with (t :: Double) (u :: String) 94 | | t >= 1e9 = printf "%.4g %s" t u 95 | | t >= 1e3 = printf "%.0f %s" t u 96 | | t >= 1e2 = printf "%.1f %s" t u 97 | | t >= 1e1 = printf "%.2f %s" t u 98 | | otherwise = printf "%.3f %s" t u 99 | 100 | -- | Simple rolling average. 101 | mean :: [Double] -> Double 102 | mean = 103 | snd . 104 | foldr 105 | (\x (cnt,avg) -> 106 | ( cnt + 1 107 | , (x + avg * cnt) / (cnt + 1))) 108 | (0, 0) 109 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # dictionaries 2 | 3 | Benchmarks for dictionary data structures: hash tables, maps, tries, 4 | etc. 5 | 6 | The `judy` package was removed from this test suite for instability; 7 | it segfaults the program. 8 | 9 | ## Running 10 | 11 | For all benchmarks: 12 | 13 | $ stack bench 14 | 15 | For just space: 16 | 17 | $ stack bench :space 18 | 19 | For just time: 20 | 21 | $ stack bench :time 22 | 23 | ## Insert Int keys space use 24 | 25 | |Case| Bytes| GCs| 26 | |---|---|---| 27 | |Data.Map.Strict.insert mempty |64 |0 | 28 | |Data.Map.Lazy.insert mempty |64 |0 | 29 | |Data.HashMap.Strict.insert mempty |64 |0 | 30 | |Data.HashMap.Lazy.insert mempty |48 |0 | 31 | 32 | ## Pure maps fromList space use 33 | 34 | | Case | Total bytes | Max residency | Final live | GCs | 35 | |------------------------------------------|---------------|---------------|------------|-------| 36 | | Data.Map.Strict.fromList (1 million) | 1,016,187,152 | 55,394,296 | 31,864 | 1,942 | 37 | | Data.Map.Lazy.fromList (1 million) | 1,016,187,152 | 55,394,296 | 31,864 | 1,942 | 38 | | Data.IntMap.Strict.fromList (1 million) | 776,852,648 | 55,207,424 | 31,864 | 1,489 | 39 | | Data.IntMap.Lazy.fromList (1 million) | 776,852,648 | 55,207,424 | 31,864 | 1,489 | 40 | | Data.HashMap.Strict.fromList (1 million) | 161,155,384 | 40,358,064 | 0 | 314 | 41 | | Data.HashMap.Lazy.fromList (1 million) | 161,155,384 | 40,358,064 | 0 | 314 | 42 | 43 | ## IO maps fromList space use 44 | 45 | | Case | Total bytes | Max residency | Final live | GCs | 46 | |-----------------------------------------------|-------------|---------------|------------|-----| 47 | | Data.HashTable.IO.BasicHashTable (1 million) | 424,214,184 | 47,254,400 | 1,120 | 672 | 48 | | Data.HashTable.IO.CuckooHashTable (1 million) | 173,581,848 | 1,328 | 1,328 | 244 | 49 | | Data.HashTable.IO.LinearHashTable (1 million) | 281,294,784 | 22,373,256 | 0 | 545 | 50 | 51 | 52 | 53 | ## Insert Int (Randomized) 54 | 55 | |Name|10|100|1000|10000| 56 | |---|---|---|---|---| 57 | |Data.Map.Lazy|406.3 ns|7.695 μs|137.3 μs|2.349 ms| 58 | |Data.Map.Strict|473.1 ns|9.485 μs|165.3 μs|2.769 ms| 59 | |Data.HashMap.Lazy|287.2 ns|3.949 μs|53.47 μs|1.741 ms| 60 | |Data.HashMap.Strict|291.8 ns|3.948 μs|53.25 μs|1.711 ms| 61 | |Data.IntMap.Lazy|136.4 ns|2.119 μs|30.13 μs|0.878 ms| 62 | |Data.IntMap.Strict|161.3 ns|2.878 μs|39.46 μs|0.985 ms| 63 | 64 | ## IO Insert Int (Randomized) 65 | 66 | |Name|10|100|1000|10000| 67 | |---|---|---|---|---| 68 | |Data.HashTable.IO.BasicHashTable|356.1 ns|3.308 μs|33.70 μs|466.2 μs| 69 | |Data.HashTable.IO.LinearHashTable|684.2 ns|7.321 μs|76.05 μs|660.1 μs| 70 | |Data.HashTable.IO.CuckooHashTable|875.3 ns|8.493 μs|85.69 μs|943.4 μs| 71 | 72 | ## Intersection (Randomized) 73 | 74 | |Name|10|100|1000|10000|100000|1000000| 75 | |---|---|---|---|---|---|---| 76 | |Data.Map.Lazy|422.4 ns|5.036 μs|63.85 μs|742.4 μs|10.38 ms|154.1 ms| 77 | |Data.Map.Strict|437.6 ns|5.028 μs|65.15 μs|742.6 μs|9.070 ms|97.95 ms| 78 | |Data.HashMap.Lazy|118.3 ns|1.332 μs|17.81 μs|225.2 μs|2.760 ms|37.95 ms| 79 | |Data.HashMap.Strict|114.4 ns|1.315 μs|17.94 μs|225.5 μs|2.884 ms|38.64 ms| 80 | |Data.IntMap.Lazy|66.73 ns|0.454 μs|5.146 μs|121.1 μs|1.533 ms|23.48 ms| 81 | |Data.IntMap.Strict|66.86 ns|0.456 μs|5.115 μs|120.2 μs|1.524 ms|24.30 ms| 82 | 83 | ## IO Intersection (Randomized) 84 | 85 | |Name|10|100|1000|10000|100000| 86 | |---|---|---|---|---|---| 87 | |Data.HashTable.IO.BasicHashTable|212.9 ns|1.286 μs|17.44 μs|368.9 μs|9.504 ms| 88 | |Data.HashTable.IO.LinearHashTable|262.8 ns|2.503 μs|25.17 μs|309.6 μs|14.84 ms| 89 | |Data.HashTable.IO.CuckooHashTable|1010 ns|8.765 μs|84.19 μs|901.9 μs|19.21 ms| 90 | 91 | ## Intersection ByteString (Randomized) 92 | 93 | |Name|10|100|1000|10000|100000|1000000| 94 | |---|---|---|---|---|---|---| 95 | |Data.Map.Lazy|947.2 ns|11.61 μs|197.5 μs|3.059 ms|46.70 ms|607.7 ms| 96 | |Data.Map.Strict|1152 ns|15.69 μs|209.6 μs|3.149 ms|41.23 ms|500.9 ms| 97 | |Data.HashMap.Lazy|533.9 ns|6.898 μs|81.07 μs|1.514 ms|24.18 ms|375.1 ms| 98 | |Data.HashMap.Strict|648.7 ns|8.636 μs|80.14 μs|1.166 ms|24.13 ms|314.8 ms| 99 | |Data.Trie|439.6 ns|6.474 μs|73.46 μs|1.755 ms|24.13 ms|245.4 ms| 100 | 101 | ## Lookup Int (Randomized) 102 | 103 | |Name|10|100|1000|10000|100000|1000000| 104 | |---|---|---|---|---|---|---| 105 | |Data.Map.Lazy|97.96 ns|1.634 μs|52.42 μs|1023 μs|20.27 ms|697.9 ms| 106 | |Data.Map.Strict|101.1 ns|1.632 μs|52.25 μs|970.9 μs|17.87 ms|583.0 ms| 107 | |Data.HashMap.Lazy|133.6 ns|1.705 μs|22.95 μs|408.9 μs|8.338 ms|453.7 ms| 108 | |Data.HashMap.Strict|132.9 ns|1.728 μs|22.42 μs|411.9 μs|8.540 ms|460.5 ms| 109 | |Data.IntMap.Lazy|105.7 ns|1.756 μs|53.45 μs|895.8 μs|14.88 ms|710.6 ms| 110 | |Data.IntMap.Strict|103.6 ns|1.688 μs|53.62 μs|883.4 μs|15.25 ms|700.8 ms| 111 | 112 | ## IO Lookup Int (Randomized) 113 | 114 | |Name|10|100|1000|10000|100000|1000000| 115 | |---|---|---|---|---|---|---| 116 | |Data.HashTable.IO.BasicHashTable|15.24 ns|23.19 ns|14.62 ns|14.43 ns|14.34 ns|14.28 ns| 117 | |Data.HashTable.IO.LinearHashTable|53.83 ns|58.41 ns|57.73 ns|53.36 ns|57.63 ns|145.0 ns| 118 | |Data.HashTable.IO.CuckooHashTable|59.15 ns|57.50 ns|57.13 ns|58.15 ns|57.69 ns|56.47 ns| 119 | 120 | ## FromList ByteString (Monotonic) 121 | 122 | |Name|10000| 123 | |---|---| 124 | |Data.Map.Lazy|3.584 ms| 125 | |Data.Map.Strict|4.161 ms| 126 | |Data.HashMap.Lazy|2.040 ms| 127 | |Data.HashMap.Strict|2.075 ms| 128 | |Data.Trie|8.717 ms| 129 | 130 | ## FromList ByteString (Randomized) 131 | 132 | |Name|10|100|1000|10000| 133 | |---|---|---|---|---| 134 | |Data.Map.Lazy|559.3 ns|11.60 μs|236.3 μs|4.496 ms| 135 | |Data.Map.Strict|631.5 ns|13.54 μs|246.4 μs|5.082 ms| 136 | |Data.HashMap.Lazy|547.8 ns|7.100 μs|96.10 μs|2.741 ms| 137 | |Data.HashMap.Strict|557.0 ns|7.214 μs|98.62 μs|2.710 ms| 138 | |Data.Trie|910.6 ns|15.71 μs|373.8 μs|14.93 ms| 139 | 140 | ## Lookup ByteString Monotonic 141 | 142 | |Name|10000| 143 | |---|---| 144 | |Data.Map.Lazy|91.88 ns| 145 | |Data.Map.Strict|91.35 ns| 146 | |Data.HashMap.Lazy|27.23 ns| 147 | |Data.HashMap.Strict|27.41 ns| 148 | |Data.Trie|150.7 ns| 149 | 150 | ## Lookup ByteString Randomized 151 | 152 | |Name|10000| 153 | |---|---| 154 | |Data.Map.Lazy|2.031 ms| 155 | |Data.Map.Strict|1.915 ms| 156 | |Data.HashMap.Lazy|0.678 ms| 157 | |Data.HashMap.Strict|0.670 ms| 158 | |Data.Trie|2.515 ms| 159 | 160 | -------------------------------------------------------------------------------- /Time.hs: -------------------------------------------------------------------------------- 1 | {-# OPTIONS_GHC -fno-warn-orphans #-} 2 | {-# LANGUAGE BangPatterns #-} 3 | {-# LANGUAGE ExistentialQuantification #-} 4 | {-# LANGUAGE StandaloneDeriving #-} 5 | 6 | module Main (main) where 7 | 8 | import Common () 9 | import Control.Arrow 10 | import Control.DeepSeq 11 | import Control.Monad 12 | import Criterion.Main 13 | import Criterion.Types 14 | import Data.ByteString (ByteString) 15 | import qualified Data.ByteString.Char8 as S8 16 | import qualified Data.HashMap.Lazy 17 | import qualified Data.HashMap.Strict 18 | import qualified Data.HashTable.Class 19 | import qualified Data.HashTable.IO 20 | import qualified Data.IntMap.Lazy 21 | import qualified Data.IntMap.Strict 22 | import Data.List (foldl') 23 | import qualified Data.Map.Lazy 24 | import qualified Data.Map.Strict 25 | import Data.Maybe (isJust) 26 | import qualified Data.Trie 27 | import qualified Data.Trie.Internal 28 | import System.Directory 29 | import System.Random 30 | 31 | data InsertInt = forall f. NFData (f Int) => InsertInt String (Int -> f Int) 32 | 33 | data InsertIntIO = forall d. NFData d => InsertIntIO String (IO d) (d -> Int -> IO d) 34 | 35 | data FromListBS = 36 | forall f. NFData (f Int) => 37 | FromListBS String 38 | ([(ByteString,Int)] -> f Int) 39 | 40 | data Intersection = forall f. NFData (f Int) => 41 | Intersection String ([(Int,Int)] -> f Int) (f Int -> f Int -> f Int) 42 | 43 | data IntersectionIO = forall d. NFData d => 44 | IntersectionIO String ([(Int,Int)] -> IO d) (d -> d -> IO d) 45 | 46 | data LookupBS = 47 | forall f. (NFData (f Int)) => 48 | LookupBS String 49 | ([(ByteString, Int)] -> f Int) 50 | (ByteString -> f Int -> (Maybe Int)) 51 | 52 | data IntersectionBS = forall f. NFData (f Int) => 53 | IntersectionBS String ([(ByteString,Int)] -> f Int) (f Int -> f Int -> f Int) 54 | 55 | data Lookup = 56 | forall f. (NFData (f Int)) => 57 | Lookup String 58 | ([(Int, Int)] -> f Int) 59 | (Int -> f Int -> (Maybe Int)) 60 | 61 | data LookupIO = 62 | forall f. NFData (f Int) => 63 | LookupIO String 64 | ([(Int, Int)] -> IO (f Int)) 65 | (f Int -> Int -> IO (Maybe Int)) 66 | 67 | -- | TODO: We need a proper deepseq. But Trie seems to perform awfully anyway so far, anyway. 68 | instance NFData (Data.Trie.Trie a) where 69 | rnf x = seq x () 70 | 71 | main :: IO () 72 | main = do 73 | let fp = "out.csv" 74 | exists <- doesFileExist fp 75 | when exists (removeFile fp) 76 | defaultMainWith 77 | defaultConfig {csvFile = Just fp} 78 | [ bgroup 79 | "Insert Int (Randomized)" 80 | (insertInts 81 | [ InsertInt "Data.Map.Lazy" insertMapLazy 82 | , InsertInt "Data.Map.Strict" insertMapStrict 83 | , InsertInt "Data.HashMap.Lazy" insertHashMapLazy 84 | , InsertInt "Data.HashMap.Strict" insertHashMapStrict 85 | , InsertInt "Data.IntMap.Lazy" insertIntMapLazy 86 | , InsertInt "Data.IntMap.Strict" insertIntMapStrict 87 | ]) 88 | , bgroup 89 | "IO Insert Int (Randomized)" 90 | (insertIntsIO 91 | [ InsertIntIO 92 | "Data.HashTable.IO.BasicHashTable" 93 | Data.HashTable.IO.new 94 | insertHashTableIOBasic 95 | , InsertIntIO 96 | "Data.HashTable.IO.LinearHashTable" 97 | Data.HashTable.IO.new 98 | insertHashTableIOLinear 99 | , InsertIntIO 100 | "Data.HashTable.IO.CuckooHashTable" 101 | Data.HashTable.IO.new 102 | insertHashTableIOCuckoo 103 | ]) 104 | , bgroup 105 | "Intersection (Randomized)" 106 | (intersection 107 | [ Intersection 108 | "Data.Map.Lazy" 109 | Data.Map.Lazy.fromList 110 | Data.Map.Lazy.intersection 111 | , Intersection 112 | "Data.Map.Strict" 113 | Data.Map.Strict.fromList 114 | Data.Map.Strict.intersection 115 | , Intersection 116 | "Data.HashMap.Lazy" 117 | Data.HashMap.Lazy.fromList 118 | Data.HashMap.Lazy.intersection 119 | , Intersection 120 | "Data.HashMap.Strict" 121 | Data.HashMap.Strict.fromList 122 | Data.HashMap.Strict.intersection 123 | , Intersection 124 | "Data.IntMap.Lazy" 125 | Data.IntMap.Lazy.fromList 126 | Data.IntMap.Lazy.intersection 127 | , Intersection 128 | "Data.IntMap.Strict" 129 | Data.IntMap.Strict.fromList 130 | Data.IntMap.Strict.intersection 131 | ]) 132 | , bgroup 133 | "IO Intersection (Randomized)" 134 | (intersectionIO 135 | [ IntersectionIO 136 | "Data.HashTable.IO.BasicHashTable" 137 | Data.HashTable.IO.fromList 138 | intersectionHashTableIOBasic 139 | , IntersectionIO 140 | "Data.HashTable.IO.LinearHashTable" 141 | Data.HashTable.IO.fromList 142 | intersectionHashTableIOLinear 143 | , IntersectionIO 144 | "Data.HashTable.IO.CuckooHashTable" 145 | Data.HashTable.IO.fromList 146 | intersectionHashTableIOCuckoo 147 | ]) 148 | , bgroup 149 | "Intersection ByteString (Randomized)" 150 | (intersectionBS 151 | [ IntersectionBS 152 | "Data.Map.Lazy" 153 | Data.Map.Lazy.fromList 154 | Data.Map.Lazy.intersection 155 | , IntersectionBS 156 | "Data.Map.Strict" 157 | Data.Map.Strict.fromList 158 | Data.Map.Strict.intersection 159 | , IntersectionBS 160 | "Data.HashMap.Lazy" 161 | Data.HashMap.Lazy.fromList 162 | Data.HashMap.Lazy.intersection 163 | , IntersectionBS 164 | "Data.HashMap.Strict" 165 | Data.HashMap.Strict.fromList 166 | Data.HashMap.Strict.intersection 167 | , IntersectionBS 168 | "Data.Trie" 169 | Data.Trie.fromList 170 | (Data.Trie.Internal.intersectBy (\a _ -> Just a)) 171 | ]) 172 | , bgroup 173 | "Lookup Int (Randomized)" 174 | (lookupRandomized 175 | [ Lookup "Data.Map.Lazy" Data.Map.Lazy.fromList Data.Map.Lazy.lookup 176 | , Lookup 177 | "Data.Map.Strict" 178 | Data.Map.Strict.fromList 179 | Data.Map.Strict.lookup 180 | , Lookup 181 | "Data.HashMap.Lazy" 182 | Data.HashMap.Lazy.fromList 183 | Data.HashMap.Lazy.lookup 184 | , Lookup 185 | "Data.HashMap.Strict" 186 | Data.HashMap.Strict.fromList 187 | Data.HashMap.Strict.lookup 188 | , Lookup 189 | "Data.IntMap.Lazy" 190 | Data.IntMap.Lazy.fromList 191 | Data.IntMap.Lazy.lookup 192 | , Lookup 193 | "Data.IntMap.Strict" 194 | Data.IntMap.Strict.fromList 195 | Data.IntMap.Strict.lookup 196 | ]) 197 | , bgroup 198 | "IO Lookup Int (Randomized)" 199 | (lookupRandomizedIO 200 | [ LookupIO 201 | "Data.HashTable.IO.BasicHashTable" 202 | (Data.HashTable.IO.fromList :: [(Int, Int)] -> IO (Data.HashTable.IO.BasicHashTable Int Int)) 203 | Data.HashTable.IO.lookup 204 | , LookupIO 205 | "Data.HashTable.IO.LinearHashTable" 206 | (Data.HashTable.IO.fromList :: [(Int, Int)] -> IO (Data.HashTable.IO.LinearHashTable Int Int)) 207 | Data.HashTable.IO.lookup 208 | , LookupIO 209 | "Data.HashTable.IO.CuckooHashTable" 210 | (Data.HashTable.IO.fromList :: [(Int, Int)] -> IO (Data.HashTable.IO.CuckooHashTable Int Int)) 211 | Data.HashTable.IO.lookup 212 | ]) 213 | , bgroup 214 | "FromList ByteString (Monotonic)" 215 | (insertBSMonotonic 216 | [ FromListBS "Data.Map.Lazy" Data.Map.Lazy.fromList 217 | , FromListBS "Data.Map.Strict" Data.Map.Strict.fromList 218 | , FromListBS "Data.HashMap.Lazy" Data.HashMap.Lazy.fromList 219 | , FromListBS "Data.HashMap.Strict" Data.HashMap.Strict.fromList 220 | , FromListBS "Data.Trie" Data.Trie.fromList 221 | ]) 222 | , bgroup 223 | "FromList ByteString (Randomized)" 224 | (insertBSRandomized 225 | [ FromListBS "Data.Map.Lazy" Data.Map.Lazy.fromList 226 | , FromListBS "Data.Map.Strict" Data.Map.Strict.fromList 227 | , FromListBS "Data.HashMap.Lazy" Data.HashMap.Lazy.fromList 228 | , FromListBS "Data.HashMap.Strict" Data.HashMap.Strict.fromList 229 | , FromListBS "Data.Trie" Data.Trie.fromList 230 | ]) 231 | , bgroup 232 | "Lookup ByteString Monotonic" 233 | (lookupBSMonotonic 234 | [ LookupBS 235 | "Data.Map.Lazy" 236 | Data.Map.Lazy.fromList 237 | Data.Map.Lazy.lookup 238 | , LookupBS 239 | "Data.Map.Strict" 240 | Data.Map.Strict.fromList 241 | Data.Map.Strict.lookup 242 | , LookupBS 243 | "Data.HashMap.Lazy" 244 | Data.HashMap.Lazy.fromList 245 | Data.HashMap.Lazy.lookup 246 | , LookupBS 247 | "Data.HashMap.Strict" 248 | Data.HashMap.Strict.fromList 249 | Data.HashMap.Strict.lookup 250 | , LookupBS "Data.Trie" Data.Trie.fromList Data.Trie.lookup 251 | ]) 252 | , bgroup 253 | "Lookup ByteString Randomized" 254 | (lookupBSRandomized 255 | [ LookupBS 256 | "Data.Map.Lazy" 257 | Data.Map.Lazy.fromList 258 | Data.Map.Lazy.lookup 259 | , LookupBS 260 | "Data.Map.Strict" 261 | Data.Map.Strict.fromList 262 | Data.Map.Strict.lookup 263 | , LookupBS 264 | "Data.HashMap.Lazy" 265 | Data.HashMap.Lazy.fromList 266 | Data.HashMap.Lazy.lookup 267 | , LookupBS 268 | "Data.HashMap.Strict" 269 | Data.HashMap.Strict.fromList 270 | Data.HashMap.Strict.lookup 271 | , LookupBS "Data.Trie" Data.Trie.fromList Data.Trie.lookup 272 | ]) 273 | ] 274 | where 275 | insertInts funcs = 276 | [ env 277 | (let !elems = 278 | force (zip (randoms (mkStdGen 0) :: [Int]) [1 :: Int .. i]) 279 | in pure elems) 280 | (\_ -> bench (title ++ ":" ++ show i) $ nf func i) 281 | | i <- [10, 100, 1000, 10000] 282 | , InsertInt title func <- funcs 283 | ] 284 | insertIntsIO funcs = 285 | [ env initial (\ht -> bench (title ++ ":" ++ show i) $ nfIO (func ht i)) 286 | | i <- [10, 100, 1000, 10000] 287 | , InsertIntIO title initial func <- funcs 288 | ] 289 | intersection funcs = 290 | [ env 291 | (let !args = 292 | force 293 | ( build (zip (randoms (mkStdGen 0) :: [Int]) [1 :: Int .. i]) 294 | , build (zip (randoms (mkStdGen 1) :: [Int]) [1 :: Int .. i])) 295 | in pure args) 296 | (\args -> bench (title ++ ":" ++ show i) $ nf (uncurry intersect) args) 297 | | i <- [10, 100, 1000, 10000, 100000, 1000000] 298 | , Intersection title build intersect <- funcs 299 | ] 300 | intersectionIO funcs = 301 | [ env 302 | (do xs <- build (zip (randoms (mkStdGen 0) :: [Int]) [1 :: Int .. i]) 303 | ys <- build (zip (randoms (mkStdGen 1) :: [Int]) [1 :: Int .. i]) 304 | return (xs, ys)) 305 | (\(~(xs, ys)) -> bench (title ++ ":" ++ show i) $ nfIO (intersect xs ys)) 306 | | i <- [10, 100, 1000, 10000, 100000] 307 | , IntersectionIO title build intersect <- funcs 308 | ] 309 | intersectionBS funcs = 310 | [ env 311 | (let !args = 312 | force 313 | -- ( build (zip (map (S8.pack.show) $ (randoms (mkStdGen 0) :: [Int])) [1 :: Int .. i]) 314 | -- , build (zip (map (S8.pack.show) $ (randoms (mkStdGen 1) :: [Int])) [1 :: Int .. i])) 315 | ( build (map 316 | (first (S8.pack . show)) 317 | (take i (zip (randoms (mkStdGen 0) :: [Int]) [1 ..]))) 318 | , build (map 319 | (first (S8.pack . show)) 320 | (take i (zip (randoms (mkStdGen 1) :: [Int]) [1 ..])))) 321 | in pure args) 322 | (\args -> bench (title ++ ":" ++ show i) $ nf (uncurry intersect) args) 323 | | i <- [10, 100, 1000, 10000, 100000, 1000000] 324 | , IntersectionBS title build intersect <- funcs 325 | ] 326 | insertBSRandomized funcs = 327 | [ env 328 | (let !elems = 329 | force 330 | (map 331 | (first (S8.pack . show)) 332 | (take i (zip (randoms (mkStdGen 0) :: [Int]) [1 ..]))) 333 | in pure elems) 334 | (\elems -> bench (title ++ ":" ++ show i) $ nf func elems) 335 | | i <- [10, 100, 1000, 10000] 336 | , FromListBS title func <- funcs 337 | ] 338 | lookupRandomized funcs = 339 | [ env 340 | (let list = take i (zip (randoms (mkStdGen 0) :: [Int]) [1 ..]) 341 | !elems = force (fromList list) 342 | in pure (list, elems)) 343 | (\(~(list, elems)) -> 344 | bench (title ++ ":" ++ show i) $ 345 | nf 346 | (\ks -> 347 | foldl' 348 | (\_ k -> 349 | case func k elems of 350 | Just !v -> v 351 | Nothing -> 0) 352 | 0 353 | ks) 354 | (map fst list)) 355 | | i <- [10, 100, 1000, 10000, 100000, 1000000] 356 | , Lookup title fromList func <- funcs 357 | ] 358 | lookupBSMonotonic funcs = 359 | [ env 360 | (let !list = 361 | force 362 | (map 363 | (first (S8.pack . show)) 364 | (take i (zip [1 :: Int ..] [1 ..]))) 365 | (!key, _) = list !! (div i 2) 366 | !elems = force (fromList list) 367 | in pure (elems, key)) 368 | (\(~(elems, key)) -> 369 | bench (title ++ ":" ++ show i) $ nf (flip func elems) key) 370 | | i <- [10000] 371 | , LookupBS title fromList func <- funcs 372 | ] 373 | lookupBSRandomized funcs = 374 | [ env 375 | (let !list = 376 | force 377 | (map 378 | (first (S8.pack . show)) 379 | (take i (zip (randoms (mkStdGen 0) :: [Int]) [1 ..]))) 380 | !elems = force (fromList list) 381 | in pure (list, elems)) 382 | (\(~(list, elems)) -> 383 | bench (title ++ ":" ++ show i) $ 384 | nf 385 | (\ks -> 386 | foldl' 387 | (\_ k -> 388 | case func k elems of 389 | Just !v -> v 390 | Nothing -> 0) 391 | 0 392 | ks) 393 | (map fst list)) 394 | | i <- [10000] 395 | , LookupBS title fromList func <- funcs 396 | ] 397 | lookupRandomizedIO funcs = 398 | [ env 399 | (let list = take i (zip (randoms (mkStdGen 0) :: [Int]) [1 ..]) 400 | (!key, _) = list !! (div i 2) 401 | in do !elems <- fmap force (fromList list) 402 | pure (elems, key)) 403 | (\(~(elems, key)) -> 404 | bench (title ++ ":" ++ show i) $ nfIO (func elems key)) 405 | | i <- [10, 100, 1000, 10000, 100000, 1000000] 406 | , LookupIO title fromList func <- funcs 407 | ] 408 | insertBSMonotonic funcs = 409 | [ env 410 | (let !elems = 411 | force 412 | (map 413 | (first (S8.pack . show)) 414 | (take i (zip [1 :: Int ..] [1 ..]))) 415 | in pure elems) 416 | (\elems -> bench (title ++ ":" ++ show i) $ nf func elems) 417 | | i <- [10000] 418 | , FromListBS title func <- funcs 419 | ] 420 | 421 | -------------------------------------------------------------------------------- 422 | -- Insert Int 423 | 424 | insertMapLazy :: Int -> Data.Map.Lazy.Map Int Int 425 | insertMapLazy n0 = go n0 mempty 426 | where 427 | go 0 acc = acc 428 | go n !acc = go (n - 1) (Data.Map.Lazy.insert n n acc) 429 | 430 | insertMapStrict :: Int -> Data.Map.Strict.Map Int Int 431 | insertMapStrict n0 = go n0 mempty 432 | where 433 | go 0 acc = acc 434 | go n !acc = go (n - 1) (Data.Map.Strict.insert n n acc) 435 | 436 | insertHashMapLazy :: Int -> Data.HashMap.Lazy.HashMap Int Int 437 | insertHashMapLazy n0 = go n0 mempty 438 | where 439 | go 0 acc = acc 440 | go n !acc = go (n - 1) (Data.HashMap.Lazy.insert n n acc) 441 | 442 | insertHashMapStrict :: Int -> Data.HashMap.Strict.HashMap Int Int 443 | insertHashMapStrict n0 = go n0 mempty 444 | where 445 | go 0 acc = acc 446 | go n !acc = go (n - 1) (Data.HashMap.Strict.insert n n acc) 447 | 448 | insertIntMapLazy :: Int -> Data.IntMap.Lazy.IntMap Int 449 | insertIntMapLazy n0 = go n0 mempty 450 | where 451 | go 0 acc = acc 452 | go n !acc = go (n - 1) (Data.IntMap.Lazy.insert n n acc) 453 | 454 | insertIntMapStrict :: Int -> Data.IntMap.Strict.IntMap Int 455 | insertIntMapStrict n0 = go n0 mempty 456 | where 457 | go 0 acc = acc 458 | go n !acc = go (n - 1) (Data.IntMap.Strict.insert n n acc) 459 | 460 | insertHashTableIO :: Data.HashTable.Class.HashTable ht 461 | => Data.HashTable.IO.IOHashTable ht Int Int 462 | -> Int 463 | -> IO (Data.HashTable.IO.IOHashTable ht Int Int) 464 | insertHashTableIO ht n0 = do 465 | mapM_ (\n -> Data.HashTable.IO.insert ht n n) [1..n0] 466 | return ht 467 | 468 | insertHashTableIOBasic :: Data.HashTable.IO.BasicHashTable Int Int 469 | -> Int 470 | -> IO (Data.HashTable.IO.BasicHashTable Int Int) 471 | insertHashTableIOBasic = insertHashTableIO 472 | 473 | insertHashTableIOCuckoo :: Data.HashTable.IO.CuckooHashTable Int Int 474 | -> Int 475 | -> IO (Data.HashTable.IO.CuckooHashTable Int Int) 476 | insertHashTableIOCuckoo = insertHashTableIO 477 | 478 | insertHashTableIOLinear :: Data.HashTable.IO.LinearHashTable Int Int 479 | -> Int 480 | -> IO (Data.HashTable.IO.LinearHashTable Int Int) 481 | insertHashTableIOLinear = insertHashTableIO 482 | 483 | intersectionHashTableIO :: Data.HashTable.Class.HashTable ht 484 | => Data.HashTable.IO.IOHashTable ht Int Int 485 | -> Data.HashTable.IO.IOHashTable ht Int Int 486 | -> IO (Data.HashTable.IO.IOHashTable ht Int Int) 487 | intersectionHashTableIO ht0 ht1 = do 488 | ht <- Data.HashTable.IO.new 489 | Data.HashTable.IO.mapM_ (\(k,v) -> 490 | isJust <$> Data.HashTable.IO.lookup ht1 k 491 | >>= \found -> when found (Data.HashTable.IO.insert ht k v)) ht0 492 | return ht 493 | 494 | intersectionHashTableIOBasic :: Data.HashTable.IO.BasicHashTable Int Int 495 | -> Data.HashTable.IO.BasicHashTable Int Int 496 | -> IO (Data.HashTable.IO.BasicHashTable Int Int ) 497 | intersectionHashTableIOBasic = intersectionHashTableIO 498 | 499 | intersectionHashTableIOCuckoo :: Data.HashTable.IO.CuckooHashTable Int Int 500 | -> Data.HashTable.IO.CuckooHashTable Int Int 501 | -> IO (Data.HashTable.IO.CuckooHashTable Int Int) 502 | intersectionHashTableIOCuckoo = intersectionHashTableIO 503 | 504 | intersectionHashTableIOLinear :: Data.HashTable.IO.LinearHashTable Int Int 505 | -> Data.HashTable.IO.LinearHashTable Int Int 506 | -> IO (Data.HashTable.IO.LinearHashTable Int Int) 507 | intersectionHashTableIOLinear = intersectionHashTableIO 508 | --------------------------------------------------------------------------------