concurrent-hashtable 0.1.2 → 0.1.3
raw patch · 4 files changed
+40/−14 lines, 4 filesPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
API changes (from Hackage documentation)
- Data.HashTable: _itemsTV :: Chain k v -> TVar [(k, v)]
- Data.HashTable: getAssocs :: Eq k => HashTable k v -> STM [(k, v)]
- Data.HashTable.Internal: getAssocs :: Eq k => HashTable k v -> STM [(k, v)]
+ Data.HashTable: readAssocs :: Eq k => HashTable k v -> STM [(k, v)]
+ Data.HashTable: readSize :: HashTable k v -> STM Int
+ Data.HashTable: readSizeIO :: HashTable k v -> IO Int
+ Data.HashTable.Internal: readAssocs :: Eq k => HashTable k v -> STM [(k, v)]
+ Data.HashTable.Internal: readLoad :: HashTable k v -> IO Int
+ Data.HashTable.Internal: readSize :: HashTable k v -> STM Int
Files
- concurrent-hashtable.cabal +2/−3
- src/Data/HashTable.hs +21/−2
- src/Data/HashTable/Internal.hs +15/−7
- test/Spec.hs +2/−2
concurrent-hashtable.cabal view
@@ -4,10 +4,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: dfbcbe83f4c3c52e2cf5b9e2db5e3898ada41cfbaf1f37a912b4cdd634a4bc05+-- hash: cd9377375669850e36b4505949d445efc12cf57a2c9bb8ea352987259c507369 name: concurrent-hashtable-version: 0.1.2+version: 0.1.3 synopsis: Thread-safe hash tables for multi-cores! description: Please see the README on GitHub at <https://github.com/pwrobinson/concurrent-hashtable#readme> category: Concurrency@@ -39,7 +39,6 @@ async >=2.2.2 && <3 , atomic-primops >=0.8.3 && <2 , base >=4.7 && <5- , containers >=0.6.0.1 && <1 , hashable >=1.2.7.0 && <2 , random >=1.1 && <2 , stm >=2.4.5.1 && <3
src/Data/HashTable.hs view
@@ -9,12 +9,29 @@ -- Portability : non-portable (requires concurrency, stm) -- -- You can find benchmarks and more information about the internals of this package here: <https://lowerbound.io/blog/2019-10-24_concurrent_hash_table_performance.html>+--+--+-- Example usage:+--+-- >> ht <- newWithDefaults 2 -- creates hash table of initial size 4+-- >> insert ht 1 "hello" -- adds key-value pair (1,"hello")+-- >> insert ht 2 "world" -- adds key-value pair (2,"world")+-- >> atomically $ readAssocs ht -- convert to a key-value list+-- > [(1,"hello"),(2,"world")]+-- >> readSizeIO ht -- returns 4+-- >> insert ht 3 "!" -- adds key-value pair (3,"!") and triggers a resize as the load fraction is ≥ 0.75+-- >> readSizeIO ht -- returns 8+-- >> atomically $ readAssocs ht -- convert to a key-value list+-- > [(1,"hello"),(3,"!"),(2,"world")]+--+-- List of atomic operations:+-- /insert/, /insertIfNotExists/, /lookup/, /delete/, /getAssocs/, /resize/+-- ---------------------------------------------------------------------- module Data.HashTable( HashTable, Chain,- _itemsTV, new, newWithDefaults, mkDefaultConfig,@@ -23,7 +40,9 @@ insert, insertIfNotExists, delete,- getAssocs+ readAssocs,+ readSizeIO,+ readSize ) where import Data.HashTable.Internal
src/Data/HashTable/Internal.hs view
@@ -11,7 +11,7 @@ -- You can find benchmarks and more information about the internals of this package here: <https://lowerbound.io/blog/2019-10-24_concurrent_hash_table_performance.html> -- -- List of atomic operations:--- /insert/, /insertIfNotExists/, /lookup/, /delete/, /getAssocs/, /resize/+-- /insert/, /insertIfNotExists/, /lookup/, /delete/, /readAssocs/, /resize/ ---------------------------------------------------------------------- {-# LANGUAGE MultiParamTypeClasses, ScopedTypeVariables #-} module Data.HashTable.Internal@@ -38,7 +38,7 @@ -- | Used for chain-hashing. data Chain k v = Chain- { _itemsTV :: TVar [(k,v)] -- ^ stores the+ { _itemsTV :: TVar [(k,v)] -- ^ stores the items for this index , _migrationStatusTV :: TVar MigrationStatus -- ^ used internally for resizing } deriving (Eq)@@ -104,9 +104,12 @@ {-# INLINABLE readSizeIO #-} readSizeIO :: HashTable k v -> IO Int readSizeIO ht = do- chainsVec <- readTVarIO $ _chainsVecTV ht- return $ V.length chainsVec+ V.length <$> readTVarIO (_chainsVecTV ht) +{-# INLINABLE readSize #-}+readSize :: HashTable k v -> STM Int+readSize ht = do+ V.length <$> readTVar (_chainsVecTV ht) -- | Resizes the hash table by scaling it according to the _scaleFactor in the configuration. resize :: (Eq k)@@ -163,7 +166,7 @@ writeTVar (_itemsTV newChain) ((k,v):newList) | (k,v) <- listOfNodes ] - + -- | Lookup the value for the key in the hash table if it exists. {-# INLINABLE lookup #-} lookup :: (Eq k) => HashTable k v -> k -> IO (Maybe v)@@ -268,16 +271,21 @@ when (migrationStatus == NotStarted) $ void $ forkIO (resize htable) +-- | The load (i.e. number of stored items) in the table. Note that this is not synchronized for performance reasons and hence might be somewhat out of date if a lot of contention is happening.+readLoad :: HashTable k v -> IO Int+readLoad htable = readIORef (_totalLoad htable)+ -- Atomically retrieves list of key-value pairs. If there is a lot of contention going on, this may be very inefficient.-getAssocs :: (Eq k)+readAssocs :: (Eq k) => HashTable k v -> STM [(k,v)]-getAssocs htable = do+readAssocs htable = do chainsVec <- readTVar $ _chainsVecTV htable let len = V.length chainsVec let getItemsForChain k = do chain <- readChainForIndex htable k readTVar (_itemsTV chain) msum <$> mapM getItemsForChain [0..len-1]+ {-# INLINABLE deleteFirstKey #-} deleteFirstKey :: Eq a => a -> [(a,b)] -> [(a,b)]
test/Spec.hs view
@@ -88,7 +88,7 @@ [ do res <- H.lookup chainTable k return $ (isJust res) && (fromJust res == a) | (k,a) <- mapAssocs ]- list2 <- run $ atomically $ H.getAssocs chainTable+ list2 <- run $ atomically $ H.readAssocs chainTable -- test if there aren't any duplicates in the Hash Table: let res2 = (not . hasDuplicates) list2 mymap <- run $ readIORef ioref@@ -106,4 +106,4 @@ print "------- Map ASSOCS List ----" print =<< (M.assocs <$> readIORef ioref) print "--------Hashtable AFTER --------"- print =<< atomically (H.getAssocs ctable)+ print =<< atomically (H.readAssocs ctable)