swisstable (empty) → 0.1.0.1
raw patch · 11 files changed
+813/−0 lines, 11 filesdep +QuickCheckdep +basedep +criterionsetup-changed
Dependencies added: QuickCheck, base, criterion, deepseq, hashable, hashtables, primitive, swisstable, tasty, tasty-discover, tasty-hunit, vector
Files
- ChangeLog.md +3/−0
- LICENSE +30/−0
- README.md +42/−0
- Setup.hs +2/−0
- bench/Main.hs +77/−0
- csrc/simd.c +21/−0
- src/Data/HashTable/IO/Swiss.hs +55/−0
- src/Data/HashTable/ST/Swiss.hs +362/−0
- swisstable.cabal +88/−0
- test/Spec.hs +1/−0
- test/Test/Basic.hs +132/−0
+ ChangeLog.md view
@@ -0,0 +1,3 @@+# Changelog for swisstable++## Unreleased changes
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright Author name here (c) 2021++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following+ disclaimer in the documentation and/or other materials provided+ with the distribution.++ * Neither the name of Author name here nor the names of other+ contributors may be used to endorse or promote products derived+ from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,42 @@+# swisstable++:warning: Currently, This library works on processors that support `AVX2` .++15564c4+```+benchmarking lookup(seq)/small/SwissTable+time 218.0 μs (217.3 μs .. 218.6 μs)+ 1.000 R² (0.999 R² .. 1.000 R²)+mean 218.5 μs (218.2 μs .. 218.8 μs)+std dev 1.074 μs (903.7 ns .. 1.357 μs)++benchmarking lookup(seq)/small/BasicHashTable+time 277.8 μs (277.5 μs .. 278.1 μs)+ 1.000 R² (1.000 R² .. 1.000 R²)+mean 277.6 μs (277.0 μs .. 277.9 μs)+std dev 1.479 μs (906.2 ns .. 2.655 μs)++benchmarking lookup(rand)/small/SwissTable+time 386.7 μs (384.2 μs .. 390.1 μs)+ 1.000 R² (1.000 R² .. 1.000 R²)+mean 394.0 μs (392.4 μs .. 395.4 μs)+std dev 5.017 μs (4.124 μs .. 6.518 μs)++benchmarking lookup(rand)/small/BasicHashTable+time 484.9 μs (483.8 μs .. 485.8 μs)+ 1.000 R² (1.000 R² .. 1.000 R²)+mean 484.7 μs (484.2 μs .. 485.2 μs)+std dev 1.557 μs (1.294 μs .. 1.956 μs)++benchmarking insert(seq)/sized/small/SwissTable+time 1.271 ms (1.265 ms .. 1.278 ms)+ 1.000 R² (1.000 R² .. 1.000 R²)+mean 1.245 ms (1.238 ms .. 1.252 ms)+std dev 24.58 μs (20.19 μs .. 29.61 μs)++benchmarking insert(seq)/sized/small/BasicHashTable+time 1.315 ms (1.311 ms .. 1.319 ms)+ 1.000 R² (1.000 R² .. 1.000 R²)+mean 1.321 ms (1.319 ms .. 1.322 ms)+std dev 5.360 μs (4.111 μs .. 6.704 μs)+```
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ bench/Main.hs view
@@ -0,0 +1,77 @@+{-# LANGUAGE BangPatterns #-}+module Main where++import Control.DeepSeq+import Control.DeepSeq (NFData)+import Control.Monad+import Criterion+import Criterion.Main+import qualified Data.HashTable.IO as H+import Data.HashTable.IO.Swiss hiding (fildM, mapM_)+import qualified Data.HashTable.ST.Basic+import qualified Data.HashTable.ST.Swiss as S+import Data.Maybe+import Prelude hiding (lookup)+import Test.QuickCheck (Gen, generate, vector)++smallKeys :: [Int]+smallKeys = [1..10000]++initSmall new insert = do+ t <- new+ mapM_ (\x -> insert t x x) smallKeys+ pure $! t+++benchSmall name new insert lookup =+ env (initSmall new insert) $ \t -> bench name $ whnfIO $ do+ mapM_ (lookup t) smallKeys+++genSmallRand = generate (vector 10000 :: Gen [Int])++initSmallRand new insert ks = do+ t <- new+ mapM_ (\x -> insert t x x) ks+ pure (ks, t)++benchSmallRand name new insert lookup ks =+ env (initSmallRand new insert ks) $ \ ~(ks, t) -> bench name $ whnfIO $ do+ mapM_ (lookup t) ks++insertSmallSeq name new insert =+ bench name $ whnfIO $ initSmall new insert++main =+ defaultMain+ [ bgroup+ "lookup(seq)"+ [ bgroup+ "small"+ [ benchSmall "SwissTable" new insert lookup+ , benchSmall "BasicHashTable" (H.new :: IO (H.BasicHashTable Int Int)) H.insert H.lookup+ ]+ ]+ , bgroup+ "lookup(rand)"+ [ env genSmallRand $ \k -> bgroup+ "small"+ [ benchSmallRand "SwissTable" new insert lookup k+ , benchSmallRand "BasicHashTable" (H.new :: IO (H.BasicHashTable Int Int)) H.insert H.lookup k+ ]+ ]+ , bgroup+ "insert(seq)"+ [ bgroup "sized"+ [ bgroup+ "small"+ [ insertSmallSeq "SwissTable" (newSized (2^16)) insert+ , insertSmallSeq "BasicHashTable" (H.newSized (2^16) :: IO (H.BasicHashTable Int Int)) H.insert+ ]+ ]+ ]+ ]++instance NFData (Data.HashTable.ST.Basic.HashTable s k v) where+ rnf x = seq x ()+instance NFData (S.Table s k v)
+ csrc/simd.c view
@@ -0,0 +1,21 @@+#include <stdio.h>+#include <immintrin.h>+#include <x86intrin.h>+#include <strings.h>++int _elm_cmp_vec(char elem, char *src){+ __m256i _list = _mm256_loadu_si256((__m256i*)src);+ __m256i _lookup = _mm256_set1_epi8(elem);+ return _mm256_movemask_epi8(_mm256_cmpeq_epi8(_list, _lookup));+}++int _load_movemask(char *src) {+ return _mm256_movemask_epi8(_mm256_loadu_si256((__m256i*)src));+}+++int _elm_add_movemask(char elem, char *src){+ __m256i _list = _mm256_loadu_si256((__m256i*)src);+ __m256i _lookup = _mm256_set1_epi8(elem);+ return _mm256_movemask_epi8(_mm256_xor_si256(_list, _lookup));+}
+ src/Data/HashTable/IO/Swiss.hs view
@@ -0,0 +1,55 @@+module Data.HashTable.IO.Swiss where++import Control.Monad.ST+import Data.HashTable.ST.Swiss as H+import Data.Hashable (Hashable)++new :: IO (Table RealWorld k v)+new = stToIO H.new+{-# INLINE new #-}++newSized :: (Int -> IO (Table RealWorld k v))+newSized = stToIO . H.newSized+{-# INLINE newSized #-}++insert :: (Hashable k, Eq k) => Table RealWorld k v -> k -> v -> IO ()+insert t k = stToIO . H.insert t k+{-# INLINE insert #-}++insert' :: (Hashable k, Eq k) => (k -> Int) -> Table RealWorld k v -> k -> v -> IO ()+insert' h t k = stToIO . H.insert' h t k+{-# INLINE insert' #-}++lookup :: ((Hashable k, Show k, Eq k) => Table RealWorld k a -> k -> IO (Maybe a))+lookup t = stToIO . H.lookup t+{-# INLINE lookup #-}++lookup' :: ((Hashable k, Show k, Eq k) => (k -> Int)+ -> Table RealWorld k a -> k -> IO (Maybe a))+lookup' h t = stToIO . H.lookup' h t+{-# INLINE lookup' #-}++delete :: ((Hashable k, Show k, Eq k) => Table RealWorld k v -> k -> IO ())+delete t = stToIO . H.delete t+{-# INLINE delete #-}+++getSize :: Table RealWorld k v -> IO Int+getSize = stToIO . H.getSize+{-# INLINE getSize #-}++mutateST :: (Eq k, Hashable k)+ => Table RealWorld k v -> k -> (Maybe v -> ST RealWorld (Maybe v, a)) -> IO a+mutateST t k = stToIO . H.mutateST t k+{-# INLINE mutateST #-}++mutate :: (Eq k, Hashable k)+ => Table RealWorld k v -> k -> (Maybe v -> (Maybe v, a)) -> IO a+mutate t k = stToIO . H.mutate t k+{-# INLINE mutate #-}++mapM_ :: ((k, v) -> ST RealWorld a) -> Table RealWorld k v -> IO ()+mapM_ f = stToIO . H.mapM_ f++foldM :: (a -> (k,v) -> ST RealWorld a) -> a -> Table RealWorld k v -> IO a+foldM f i = stToIO . H.foldM f i
+ src/Data/HashTable/ST/Swiss.hs view
@@ -0,0 +1,362 @@+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE ScopedTypeVariables #-}+module Data.HashTable.ST.Swiss+ ( Table (..)+ , new+ , newSized+ , insert'+ , insert+ , lookup'+ , lookup+ , delete'+ , delete+ , foldM+ , mapM_+ , analyze+ , getSize+ , mutateST+ , mutate+ ) where++import Control.Monad (forM_, void, when)+import qualified Control.Monad as M+import Control.Monad.ST (RealWorld, ST)+import Data.Bits+import Data.Hashable+import Data.Primitive+import Data.Primitive.Array as A+import Data.Primitive.Ptr as PP+import Data.STRef+import Data.Word+import Foreign.C.Types+import GHC.Generics (Generic)+import GHC.IO (ioToST)+import Prelude hiding (lookup, mapM_)++-- todo: try foreign import prim+foreign import ccall unsafe "_elm_cmp_vec" cElmCmpVec :: Word8 -> Ptr Word8 -> Word32+foreign import ccall unsafe "_load_movemask" cLoadMovemask :: Ptr Word8 -> Word32+foreign import ccall unsafe "ffs" cFfs :: Word32 -> CInt+foreign import ccall unsafe "_elm_add_movemask" cElmAddMovemask :: Word8 -> Ptr Word8 -> Word32++newtype Table s k v = T (STRef s (Table_ s k v))+ deriving (Generic)++-- todo: distibute STRef+data Table_ s k v = Table+ { elems :: {-# UNPACK #-} !(MutableArray s (k, v))+ , ctrl :: {-# UNPACK #-} !(MutablePrimArray s Word8)+ , size :: {-# UNPACK #-} !Int+ , mask :: {-# UNPACK #-} !Int+ , used :: {-# UNPACK #-} !(STRef s Int)+ } deriving (Generic)++new :: ST s (Table s k v)+new = newSized 16++empty :: Word8+empty = 128++deleted :: Word8+deleted = 254++newSized :: Int -> ST s (Table s k v)+newSized n = do+ when (n .&. (n - 1) /= 0) $ error "size should be power of 2"+ es <- A.newArray n (error "impossible")+ c <- newPinnedPrimArray (n + 32)+ setPrimArray c 0 n empty+ setPrimArray c n 32 deleted+ u <- newSTRef 0+ let t = Table es c (fromIntegral n) (fromIntegral n - 1) u+ newRef t++newRef :: Table_ s k v -> ST s (Table s k v)+newRef = fmap T . newSTRef+{-# INLINE newRef #-}++readRef :: Table s k v -> ST s (Table_ s k v)+readRef (T ref) = readSTRef ref+{-# INLINE readRef #-}++writeRef :: Table s k v -> Table_ s k v -> ST s ()+writeRef (T ref) = writeSTRef ref+{-# INLINE writeRef #-}++insert' :: (Hashable k, Eq k) => (k -> Int) -> Table s k v -> k -> v -> ST s ()+insert' h m k v = do+ mutateST' h m k (const $ pure (Just v, ()))+{-# INLINE insert' #-}++rawInsert :: (Hashable k, Eq k) => Int -> Table s k v -> k -> v -> ST s ()+rawInsert !h1' ref !k !v = do+ m@Table{..} <- readRef ref+ iterateCtrlIdx (f (mutablePrimArrayContents ctrl) size elems ctrl) size (mask .&. h1')+ modifySTRef' used (+ 1)+ checkOverflow m >>= \x -> when x $ grow ref+ where+ f !ptr !size !elems !ctrl !idx = do+ let !pc = PP.advancePtr ptr idx+ let !mask = cLoadMovemask pc+ let !offset = cFfs mask - 1+ let !idx' = idx + fromIntegral offset+ if offset >= 0 && idx' < size then do+ writeArray elems idx' (k, v)+ writePrimArray ctrl idx' (h2 h1')+ pure $ Just ()+ else pure Nothing+ {-# INLINE f #-}+{-# INLINE rawInsert #-}++lookup' :: forall k s a. (Hashable k, Eq k) => (k -> Int) -> Table s k a -> k -> ST s (Maybe a)+lookup' h !r !k = fmap fst <$> lookup'' (h1 h k) r k+{-# INLINE lookup' #-}++lookup'' :: forall k s a. (Hashable k, Eq k) => Int -> Table s k a -> k -> ST s (Maybe (a, Int))+lookup'' !h1' ref !k = do+ Table{..} <- readRef ref+ let !idx = mask .&. h1'+ iterateCtrlIdx (lookCtrlAt (mutablePrimArrayContents ctrl) elems) size idx+ where+ !h2' = h2 h1'+ lookBitmask es idx bidx = do+ let idx' = idx + bidx - 1+ (!k', v) <- readArray es idx'+ pure $ if k == k' -- todo: opt(hashも保持?)+ then Just (v, idx')+ else Nothing+ {-# INLINE lookBitmask #-}+ lookCtrlAt !ptr !es !idx = do+ let pc = PP.advancePtr ptr idx+ let !mask = cElmCmpVec h2' pc+ x <- iterateBitmaskSet (lookBitmask es idx) mask+ case x of+ Nothing+ | cElmCmpVec 128 pc /= 0 -> pure (Just Nothing) -- found empty -- unlikely+ | otherwise -> pure Nothing+ _ -> pure (Just x)+ {-# INLINE lookCtrlAt #-}+{-# INLINE lookup'' #-}++iterateCtrlIdx :: Monad m => (Int -> m (Maybe b)) -> Int -> Int -> m b+iterateCtrlIdx f !s !offset = go offset+ where+ go !idx = do+ f idx >>= \case+ Nothing ->+ let !next = idx + 32+ in if next > s then go 0 else go next+ Just x -> pure x+{-# INLINE iterateCtrlIdx #-}++listBitmaskSet :: Word32 -> [CInt]+listBitmaskSet = map cFfs . iterate (\x -> x .&. (x - 1))+{-# INLINE listBitmaskSet #-}++iterateBitmaskSet :: Monad m => (Int -> m (Maybe a)) -> Word32 -> m (Maybe a)+iterateBitmaskSet !f !mask = do+ let bitidxs = listBitmaskSet mask+ go bitidxs+ where+ go (bidx:bidxs)+ | bidx /= 0 = do+ f (fromIntegral bidx) >>= \case+ Nothing -> go bidxs+ x -> pure x+ | otherwise = pure Nothing+ go _ = pure Nothing+ {-# INLINE go #-}+{-# INLINE iterateBitmaskSet #-}++h1 :: Hashable k => (k -> Int) -> k -> Int+h1 = ($)+{-# INLINE h1 #-}++h2 :: Int -> Word8+h2 x = fromIntegral $ x .&. 127+{-# INLINE h2 #-}++-- delete :: (PrimMonad m, Hashable k, Eq k) => k -> Table (PrimState m) k v -> m ()+delete :: (Hashable k, Eq k) => Table s k v -> k -> ST s ()+delete = delete' hash++-- delete' :: (PrimMonad m, Hashable k, Eq k) => (k -> Int) -> k -> Table (PrimState m) k v -> m ()+delete' :: (Hashable k, Eq k) => (k -> Int) -> Table s k v -> k -> ST s ()+delete' hash' ref k = do+ m <- readRef ref+ let s = size m+ let h1' = h1 hash' k+ h2' = h2 h1'+ let idx = (s - 1) .&. h1'+ let es = elems m+ let ct = ctrl m+ let f'' offset = do+ let pc = PP.advancePtr (mutablePrimArrayContents ct) offset+ let mask = cElmCmpVec h2' pc+ iterateBitmaskSet (readBM es offset) mask >>= \case+ Nothing+ | cElmCmpVec 128 pc /= 0 -> pure (Just Nothing)+ | otherwise -> pure Nothing+ x -> pure (Just x)+ idx' <- iterateCtrlIdx f'' s idx+ forM_ idx' $ deleteIdx m+ where+ readBM es offset bidx = do+ let idx' = offset + bidx - 1+ (k', _) <- readArray es idx'+ pure $ if k == k'+ then Just idx'+ else Nothing++deleteIdx :: Table_ s k v+ -> Int+ -> ST s ()+deleteIdx m idx = do+ writePrimArray (ctrl m) idx 254+ modifySTRef (used m) (\x -> x - 1)++-- insert :: (PrimMonad m, Hashable k) => k -> v -> Table (PrimState m) k v -> m ()+insert :: (Hashable k, Eq k) => Table s k v -> k -> v -> ST s ()+insert = insert' hash++-- lookup :: (PrimMonad m, Hashable k, Show k, Eq k)+-- => k -> Table (PrimState m) k v -> m (Maybe v)+lookup :: (Hashable k, Eq k) => Table s k a -> k -> ST s (Maybe a)+lookup = lookup' hash+{-# INLINE lookup #-}++checkOverflow ::+ (Hashable k) => Table_ s k v -> ST s Bool+checkOverflow t = do+ u <- readSTRef (used t)+ pure $ fromIntegral u / fromIntegral (size t) > maxLoad+{-# INLINE checkOverflow #-}++maxLoad :: Double+maxLoad = 0.8++grow :: (Hashable k, Eq k) => Table s k v -> ST s ()+grow ref = do+ t <- readRef ref+ let size' = size t * 2+ t' <- newSized size'+ mapM_ (f t') ref+ writeRef ref =<< readRef t'+ pure ()+ where+ f t (k, v) = insert t k v++mapM_ :: ((k, v) -> ST s a) -> Table s k v -> ST s ()+mapM_ f ref = do+ t <- readRef ref+ let idx = 0+ void $ iterateCtrlIdx (h t) (size t) idx+ where+ g t idx bidx = do+ let idx' = idx + bidx - 1+ e <- readArray (elems t) idx'+ void $ f e+ pure Nothing+ h t idx = do+ let pc = PP.advancePtr (mutablePrimArrayContents (ctrl t)) idx+ let mask = cElmAddMovemask 128 pc+ r <- iterateBitmaskSet (g t idx) mask+ if idx + 32 > size t then pure (Just Nothing) else pure r++foldM :: (a -> (k,v) -> ST s a) -> a -> Table s k v -> ST s a+foldM f seed0 ref = do+ t <- readRef ref+ foldCtrlM g seed0 t 0+ where+ g acc t idx (bidx:xs)+ | bidx == 0 = pure acc+ | otherwise = do+ let idx' = idx + bidx - 1+ e <- readArray (elems t) idx'+ acc' <- f acc e+ g acc' t idx xs+ g _ _ _ _ = error "impossible"++foldCtrlM :: (a -> Table_ s k v -> Int -> [Int] -> ST s a) -> a -> Table_ s k v -> Int -> ST s a+foldCtrlM g acc t idx = do+ let pc = PP.advancePtr (mutablePrimArrayContents (ctrl t)) idx+ let mask = cElmAddMovemask 128 pc+ acc' <- g acc t idx (map fromIntegral $ listBitmaskSet mask)+ if idx + 32 > size t then pure acc' else foldCtrlM g acc' t (idx + 32)++_foldM :: (a -> (k,v) -> Int -> ST s a) -> a -> Table s k v -> ST s a+_foldM f seed0 ref = do+ t <- readRef ref+ foldCtrlM g seed0 t 0+ where+ g acc t idx (bidx:xs)+ | bidx == 0 = pure acc+ | otherwise = do+ let idx' = idx + bidx - 1+ e <- readArray (elems t) idx'+ acc' <- f acc e idx'+ g acc' t idx xs+ g _ _ _ _ = error "impossible"++analyze :: (Hashable k, Show k) => (Table RealWorld k v -> ST RealWorld ())+analyze ref = do+ t <- readRef ref+ cs <- _foldM (f t) [] ref+ u <- readSTRef (used t)+ ioToST $ do+ putStrLn $ "size: " <> show (size t)+ putStrLn $ "used: " <> show u+ putStrLn $ " " <> show (fromIntegral u / fromIntegral (size t) :: Double)+ print $ "max diff: " <> show (maximum (fmap snd cs))+ print $ "sum diff: " <> show (sum (fmap snd cs))+ M.mapM_ print cs+ where+ f t acc (k, _) idx = do+ let nidx = (size t - 1) .&. hash k+ let d = if idx - nidx < 0 then idx - nidx + size t else idx - nidx+ pure $ ((k, nidx, idx), d):acc++mutateST' :: (Eq k, Hashable k)+ => (k -> Int) -> Table s k v -> k -> (Maybe v -> ST s (Maybe v, a)) -> ST s a+mutateST' h ref k f = do+ t <- readRef ref+ let !h1' = h1 h k+ lookup'' h1' ref k >>= \case+ Just (v, idx) ->+ f (Just v) >>= \case+ (Just v', a) -> -- update+ writeArray (elems t) idx (k, v') >> pure a+ (Nothing, a) -> --delete+ deleteIdx t idx >> pure a+ Nothing ->+ f Nothing >>= \case+ (Just v', a) -> -- insert+ rawInsert h1' ref k v' >> pure a+ (Nothing, a) -> pure a+{-# INLINE mutateST' #-}++mutateST :: (Eq k, Hashable k)+ => Table s k v -> k -> (Maybe v -> ST s (Maybe v, a)) -> ST s a+mutateST = mutateST' hash+{-# INLINE mutateST #-}++mutate :: (Eq k, Hashable k) =>+ Table s k v -> k -> (Maybe v -> (Maybe v, a)) -> ST s a+mutate ref !k !f = mutateST ref k (pure . f)+{-# INLINE mutate #-}++{-+試したい+ 右端で競合が発生した際に0に戻るのではなく、+ 予備領域を使い、予備領域が埋まったら拡張する。+ -> unlikelyすぎて効果うすそう+-- make Data.HashTable.Class instance?+ -> 両方に依存したインターフェス揃えるようlibrary作れば良い+-}++getSize :: Table s k v -> ST s Int+getSize = fmap size . readRef
+ swisstable.cabal view
@@ -0,0 +1,88 @@+cabal-version: 1.12++-- This file has been generated from package.yaml by hpack version 0.33.0.+--+-- see: https://github.com/sol/hpack+--+-- hash: 2f9c118c109be85a1779a0713e0192b5bb52fcc83774f4555b155cc5e188a863++name: swisstable+version: 0.1.0.1+synopsis: Google's SwissTable hash map+description: Please see the README on GitHub at <https://github.com/nakaji-dayo/hs-swisstable#readme>+category: Data+homepage: https://github.com/nakaji-dayo/hs-swisstable#readme+bug-reports: https://github.com/nakaji-dayo/hs-swisstable/issues+author: Daishi Nakajima+maintainer: nakaji.dayo@gmail.com+copyright: 2021 Daishi Nakajima+license: BSD3+license-file: LICENSE+build-type: Simple+extra-source-files:+ README.md+ ChangeLog.md+ csrc/simd.c++source-repository head+ type: git+ location: https://github.com/nakaji-dayo/hs-swisstable++library+ exposed-modules:+ Data.HashTable.IO.Swiss+ Data.HashTable.ST.Swiss+ other-modules:+ Paths_swisstable+ hs-source-dirs:+ src+ ghc-options: -O2 -Wall+ cc-options: -mavx2+ c-sources:+ csrc/simd.c+ build-depends:+ base >=4.7 && <5+ , hashable+ , primitive+ , vector+ default-language: Haskell2010++executable swisstable-bench+ main-is: Main.hs+ other-modules:+ Paths_swisstable+ hs-source-dirs:+ bench+ ghc-options: -O2+ build-depends:+ QuickCheck+ , base >=4.7 && <5+ , criterion+ , deepseq+ , hashable+ , hashtables+ , primitive+ , swisstable+ , vector+ default-language: Haskell2010++test-suite swisstable-test+ type: exitcode-stdio-1.0+ main-is: Spec.hs+ other-modules:+ Test.Basic+ Paths_swisstable+ hs-source-dirs:+ test+ ghc-options: -threaded -rtsopts -with-rtsopts=-N+ build-depends:+ QuickCheck+ , base >=4.7 && <5+ , hashable+ , primitive+ , swisstable+ , tasty+ , tasty-discover+ , tasty-hunit+ , vector+ default-language: Haskell2010
+ test/Spec.hs view
@@ -0,0 +1,1 @@+{-# OPTIONS_GHC -F -pgmF tasty-discover #-}
+ test/Test/Basic.hs view
@@ -0,0 +1,132 @@+module Test.Basic where++import Prelude hiding (lookup)++import Control.Monad+import Data.HashTable.IO.Swiss hiding (foldM, mapM_)+import qualified Data.HashTable.IO.Swiss as S+import Test.Tasty+import Test.Tasty.HUnit++import Control.Monad.ST (stToIO)+import Data.Primitive.Array as A+import Data.Primitive.PrimArray+import Test.QuickCheck (Gen, generate, vector)+++unit_insertAndLookup :: IO ()+unit_insertAndLookup = do+ let ks = ["A", "Z", "C", "Y", "E", "X", "G", "W"]+ ref <- new+ mapM_ (\k -> insert ref k k) ks+ forM_ ks $ \k -> do+ h <- lookup ref k+ Just k @=? h++unit_insertAndLookup_rand :: IO ()+unit_insertAndLookup_rand = do+ ks <- generate (vector 1000 :: Gen [Int])+ ref <- new+ mapM_ (\k -> insert ref k k) ks+ forM_ ks $ \k -> do+ h <- lookup ref k+ Just k @=? h++unit_insert_conflict :: IO ()+unit_insert_conflict = do+ let ks = ["head", "Z", "C", "last"]+ t <- newSized 8+ mapM_ (\x -> insert' h t x x) ks+ forM_ ks $ \k -> do+ h <- lookup' h t k+ Just k @=? h+ where+ h = const 0++unit_insert_right_overflow :: IO ()+unit_insert_right_overflow = do+ let ks = ["head", "Z", "C", "last"]+ t <- newSized 8+ mapM_ (\x -> insert' h t x x) ks+ forM_ ks $ \k -> do+ h <- lookup' h t k+ Just k @=? h+ where+ h = const 7++-- 一旦deleteでごまかす+-- vのswapできるようにすべき+unit_update :: IO ()+unit_update = do+ let ks = take 5 $ repeat "A"+ t <- newSized 4+ mapM_ (\x -> insert t x x) ks+ s <- getSize t+ 4 @=? s++unit_lookup_nothing_conflict :: IO ()+unit_lookup_nothing_conflict = do+ let ks = ["A", "B", "C", "D"]+ t <- new+ mapM_ (\x -> insert' h t x x) ks+ h <- lookup' h t "X"+ h @=? Nothing+ where+ h = const 7++unit_lookup_nothing :: IO ()+unit_lookup_nothing = do+ let ks = ["A", "B", "C", "D"]+ t <- new+ mapM_ (\x -> insert t x x) ks+ h <- lookup t "X"+ h @=? Nothing++unit_grow_rehash :: IO ()+unit_grow_rehash = do+ let ks = ["A","Z", "C", "Y", "E", "X", "G", "W", "ab", "cd", "ef", "gh", "xx"]+ t <- newSized 8+ mapM_ (\k -> insert t k k) ks+ forM_ ks $ \k -> do+ h <- lookup t k+ Just k @=? h++unit_grow_rehash2 :: IO ()+unit_grow_rehash2 = do+ let ks = [0..99::Int]+ t <- newSized 8+ mapM_ (\k -> insert t k k) ks+ forM_ ks $ \k -> do+ h <- lookup t k+ Just k @=? h++unit_delete :: IO ()+unit_delete = do+ let ks = ["A","B", "C"]+ t <- new+ mapM_ (\k -> insert t k k) ks+ delete t "B"+ h <- lookup t "B"+ h @=? Nothing+ h <- lookup t "C"+ h @=? Just "C"++unit_foldM :: IO ()+unit_foldM = do+ let ks = ["A","B", "C"]+ t <- new+ mapM_ (\k -> insert t k k) ks+ x <- S.foldM (\acc (k, _) -> pure (acc ++ k)) "" t+ "ABC" @=? x++unit_mutate :: IO ()+unit_mutate = do+ let ks = ["A","B", "C"]+ t <- new+ mapM_ (\k -> insert t k k) ks+ mutate t "A" (\(Just v) -> (Just (v ++ "!"), ()))+ mutate t "B" (const (Nothing, ()))+ a <- lookup t "A"+ Just "A!" @=? a+ a <- lookup t "B"+ Nothing @=? a