stm-containers 0.1.2 → 0.1.3
raw patch · 14 files changed
+350/−89 lines, 14 filesdep +base-preludedep ~base
Dependencies added: base-prelude
Dependency ranges changed: base
Files
- executables/APITests.hs +1/−1
- executables/ConcurrentInsertionBench.hs +1/−1
- executables/ConcurrentTransactionsBench.hs +4/−4
- executables/WordArrayTests.hs +4/−0
- library/STMContainers/Bimap.hs +133/−0
- library/STMContainers/HAMT/Nodes.hs +2/−4
- library/STMContainers/Map.hs +2/−2
- library/STMContainers/Multimap.hs +131/−0
- library/STMContainers/Prelude.hs +5/−45
- library/STMContainers/Set.hs +23/−7
- library/STMContainers/SizedArray.hs +5/−0
- library/STMContainers/WordArray.hs +5/−10
- library/STMContainers/WordArray/Indices.hs +12/−5
- stm-containers.cabal +22/−10
executables/APITests.hs view
@@ -1,7 +1,7 @@ {-# OPTIONS_GHC -F -pgmF htfpp #-} import Test.Framework-import STMContainers.Prelude+import BasePrelude import {-@ HTF_TESTS @-} APITests.MapTests
executables/ConcurrentInsertionBench.hs view
@@ -128,4 +128,4 @@ ] where actionsNum = 100000- threadsNums = [1, 2, 4, 6, 8, 12, 16, 32]+ threadsNums = [1, 2, 4, 6, 8, 12, 16, 32, 40, 52, 64, 80, 128]
executables/ConcurrentTransactionsBench.hs view
@@ -192,7 +192,7 @@ -- Pregenerate the transactions: transactionsGroups <- MWC.runWithCreate $ replicateM (maximum threadsNums) $- transactionsGroupGenerator (actionsNum `div` (maximum threadsNums))+ transactionsGroupGenerator (transactionsNum `div` (maximum threadsNums)) -- Run the benchmark: defaultMain $! flip map threadsNums $ \threadsNum -> @@ -201,8 +201,8 @@ map concat $! slices (length transactionsGroups `div` threadsNum) transactionsGroups in- bench (shows threadsNum . showString "/" . shows (actionsNum `div` threadsNum) $ "") $+ bench (shows threadsNum . showString "/" . shows (transactionsNum `div` threadsNum) $ "") $ scSessionRunner specializedSCInterpreter session where- threadsNums = [1, 2, 4, 6, 8, 12, 16, 32, 64, 128]- actionsNum = 200000+ threadsNums = [1, 2, 4, 6, 8, 12, 16, 32, 40, 52, 64, 80, 128]+ transactionsNum = 400000
executables/WordArrayTests.hs view
@@ -34,3 +34,7 @@ where w = WordArray.fromList [(3, 'a'), (7, 'b'), (14, 'c')] +test_foldable = do+ assertEqual "abdc" $ toList w+ where+ w = WordArray.fromList [(3, 'a'), (7, 'b'), (8, 'd'), (14, 'c')]
+ library/STMContainers/Bimap.hs view
@@ -0,0 +1,133 @@+module STMContainers.Bimap+(+ Bimap,+ Association,+ new,+ insert1,+ insert2,+ delete1,+ delete2,+ lookup1,+ lookup2,+ focus1,+ focus2,+ foldM,+ null,+)+where++import STMContainers.Prelude hiding (insert, delete, lookup, alter, foldM, toList, empty, null)+import qualified Focus+import qualified STMContainers.Map as Map+++-- |+-- A bidirectional map.+-- Essentially a bijection between subsets of its two argument types.+-- +-- For one value of a left-hand type there this map contains one value +-- of the right-hand type and vice versa.+data Bimap a b = + Bimap {m1 :: !(Map.Map a b), m2 :: !(Map.Map b a)}++-- |+-- A standard constraint for associations.+type Association a b = (Map.Key a, Map.Key b)++-- |+-- Construct a new bimap.+{-# INLINABLE new #-}+new :: STM (Bimap a b)+new = Bimap <$> Map.new <*> Map.new++-- |+-- Check on being empty.+{-# INLINABLE null #-}+null :: Bimap a b -> STM Bool+null = Map.null . m1++-- |+-- Look up a right value by a left value.+{-# INLINABLE lookup1 #-}+lookup1 :: (Association a b) => a -> Bimap a b -> STM (Maybe b)+lookup1 k = Map.lookup k . m1++-- |+-- Look up a left value by a right value.+{-# INLINABLE lookup2 #-}+lookup2 :: (Association a b) => b -> Bimap a b -> STM (Maybe a)+lookup2 k = Map.lookup k . m2++-- |+-- Insert an association by a left value.+{-# INLINABLE insert1 #-}+insert1 :: (Association a b) => b -> a -> Bimap a b -> STM ()+insert1 b a (Bimap m1 m2) = + do+ Map.insert b a m1+ Map.insert a b m2++-- |+-- Insert an association by a right value.+{-# INLINABLE insert2 #-}+insert2 :: (Association a b) => a -> b -> Bimap a b -> STM ()+insert2 b a (Bimap m1 m2) = (inline insert1) b a (Bimap m2 m1)++-- |+-- Delete an association by a left value.+{-# INLINABLE delete1 #-}+delete1 :: (Association a b) => a -> Bimap a b -> STM ()+delete1 k (Bimap m1 m2) =+ Map.focus lookupAndDeleteStrategy k m1 >>= + mapM_ (\k' -> Map.delete k' m2)+ where+ lookupAndDeleteStrategy r =+ return (r, Focus.Remove)++-- |+-- Delete an association by a right value.+{-# INLINABLE delete2 #-}+delete2 :: (Association a b) => b -> Bimap a b -> STM ()+delete2 k (Bimap m1 m2) = (inline delete1) k (Bimap m2 m1)++-- |+-- Focus on a right value by a left value with a strategy.+-- +-- This function allows to perform composite operations in a single access+-- to a map item.+-- E.g., you can look up an item and delete it at the same time,+-- or update it and return the new value.+{-# INLINABLE focus1 #-}+focus1 :: (Association a b) => Focus.StrategyM STM b r -> a -> Bimap a b -> STM r+focus1 s a (Bimap m1 m2) =+ do + (r, d, mb) <- Map.focus s' a m1+ case d of+ Focus.Keep -> + return ()+ Focus.Remove -> + forM_ mb $ \b -> Map.delete b m2+ Focus.Replace b' ->+ do+ forM_ mb $ \b -> Map.delete b m2+ Map.insert a b' m2+ return r+ where+ s' = \k -> s k >>= \(r, d) -> return ((r, d, k), d)++-- |+-- Focus on a left value by a right value with a strategy.+-- +-- This function allows to perform composite operations in a single access+-- to a map item.+-- E.g., you can look up an item and delete it at the same time,+-- or update it and return the new value.+{-# INLINABLE focus2 #-}+focus2 :: (Association a b) => Focus.StrategyM STM a r -> b -> Bimap a b -> STM r+focus2 s b (Bimap m1 m2) = (inline focus1) s b (Bimap m2 m1)++-- |+-- Fold all the associations.+{-# INLINABLE foldM #-}+foldM :: (r -> (a, b) -> STM r) -> r -> Bimap a b -> STM r+foldM s r = Map.foldM s r . m1
library/STMContainers/HAMT/Nodes.hs view
@@ -1,6 +1,7 @@ module STMContainers.HAMT.Nodes where import STMContainers.Prelude hiding (insert, lookup, delete, foldM, null)+import qualified STMContainers.Prelude as Prelude import qualified STMContainers.WordArray as WordArray import qualified STMContainers.SizedArray as SizedArray import qualified STMContainers.HAMT.Level as Level@@ -134,16 +135,13 @@ return (Focus.Replace (Nodes ns')) _ -> return Focus.Keep- -- | A replacement for the missing 'Traverse' instance of pair in base < 4.7.- traversePair f (x, y) = (,) x <$> f y- null :: Nodes e -> STM Bool null = fmap WordArray.null . readTVar foldM :: (a -> e -> STM a) -> a -> Level.Level -> Nodes e -> STM a foldM step acc level = - readTVar >=> WordArray.foldM step' acc+ readTVar >=> foldlM step' acc where step' acc' = \case Nodes ns -> foldM step acc' (Level.succ level) ns
library/STMContainers/Map.hs view
@@ -45,7 +45,7 @@ lookup k = focus Focus.lookupM k -- |--- Insert a key and a value.+-- Insert a value at a key. {-# INLINE insert #-} insert :: (Key k) => v -> k -> Map k v -> STM () insert !v !k (Map h) = HAMT.insert (k, v) h@@ -61,7 +61,7 @@ -- -- This function allows to perform composite operations in a single access -- to a map item.--- E.g., you can lookup an item and delete it at the same time,+-- E.g., you can look up an item and delete it at the same time, -- or update it and return the new value. {-# INLINE focus #-} focus :: (Key k) => Focus.StrategyM STM v r -> k -> Map k v -> STM r
+ library/STMContainers/Multimap.hs view
@@ -0,0 +1,131 @@+module STMContainers.Multimap+(+ Multimap,+ Association,+ new,+ insert,+ delete,+ lookup,+ focus,+ foldM,+ null,+)+where++import STMContainers.Prelude hiding (insert, delete, lookup, alter, foldM, toList, empty, null)+import qualified Focus+import qualified STMContainers.Map as Map+import qualified STMContainers.Set as Set+++-- |+-- A multimap, based on an STM-specialized hash array mapped trie.+-- +-- Basically it's just a wrapper API around @'Map.Map' k ('Set.Set' v)@.+newtype Multimap k v = Multimap (Map.Map k (Set.Set v))++-- |+-- A standard constraint for items.+type Association k v = (Eq k, Hashable k, Eq v, Hashable v)++-- |+-- Look up an item by a value and a key.+{-# INLINE lookup #-}+lookup :: (Association k v) => v -> k -> Multimap k v -> STM Bool+lookup v k (Multimap m) = + maybe (return False) (Set.lookup v) =<< Map.lookup k m++-- |+-- Insert an item.+{-# INLINABLE insert #-}+insert :: (Association k v) => v -> k -> Multimap k v -> STM ()+insert v k (Multimap m) =+ Map.focus ms k m+ where+ ms = + \case + Just s -> + do+ Set.insert v s+ return ((), Focus.Keep)+ Nothing ->+ do+ s <- Set.new+ Set.insert v s+ return ((), Focus.Replace s)++-- |+-- Delete an item by a value and a key.+{-# INLINABLE delete #-}+delete :: (Association k v) => v -> k -> Multimap k v -> STM ()+delete v k (Multimap m) =+ Map.focus ms k m+ where+ ms = + \case + Just s -> + do+ Set.delete v s+ Set.null s >>= returnDecision . bool Focus.Keep Focus.Remove+ Nothing ->+ returnDecision Focus.Keep+ where+ returnDecision c = return ((), c)++-- |+-- Focus on an item with a strategy by a value and a key.+-- +-- This function allows to perform simultaneous lookup and modification.+-- +-- The strategy is over a unit since we already know,+-- which value we're focusing on and it doesn't make sense to replace it,+-- however we still can still decide wether to keep or remove it.+{-# INLINE focus #-}+focus :: (Association k v) => Focus.StrategyM STM () r -> v -> k -> Multimap k v -> STM r+focus = + \s v k (Multimap m) -> Map.focus (liftSetItemStrategy v s) k m+ where+ liftSetItemStrategy :: + (Set.Element e) => e -> Focus.StrategyM STM () r -> Focus.StrategyM STM (Set.Set e) r+ liftSetItemStrategy e s =+ \case+ Nothing ->+ traversePair liftDecision =<< s Nothing+ where+ liftDecision =+ \case+ Focus.Replace b ->+ do+ s <- Set.new+ Set.insert e s+ return (Focus.Replace s)+ _ ->+ return Focus.Keep+ Just set -> + do+ r <- Set.focus s e set+ (r,) . bool Focus.Keep Focus.Remove <$> Set.null set++-- |+-- Fold all the items.+{-# INLINE foldM #-}+foldM :: (a -> (k, v) -> STM a) -> a -> Multimap k v -> STM a+foldM f a (Multimap m) = + Map.foldM f' a m+ where+ f' a' (k, set) = + Set.foldM f'' a' set+ where+ f'' a'' v = f a'' (k, v)++-- |+-- Construct a new multimap.+{-# INLINE new #-}+new :: STM (Multimap k v)+new = Multimap <$> Map.new++-- |+-- Check on being empty.+{-# INLINE null #-}+null :: Multimap k v -> STM Bool+null (Multimap m) = Map.null m
library/STMContainers/Prelude.hs view
@@ -3,53 +3,13 @@ module Exports, bug, bottom,- bool,+ traversePair, ) where -- base --------------------------import Prelude as Exports hiding (concat, foldr, mapM_, sequence_, foldl1, maximum, minimum, product, sum, all, and, any, concatMap, elem, foldl, foldr1, notElem, or, mapM, sequence, FilePath, id, (.))-import Control.Monad as Exports hiding (mapM_, sequence_, forM_, msum, mapM, sequence, forM)-import Control.Applicative as Exports-import Control.Arrow as Exports hiding (left, right)-import Control.Category as Exports-import Data.Monoid as Exports-import Data.Foldable as Exports-import Data.Traversable as Exports hiding (for)-import Data.Maybe as Exports-import Data.Either as Exports-import Data.List as Exports hiding (concat, foldr, foldl1, maximum, minimum, product, sum, all, and, any, concatMap, elem, foldl, foldr1, notElem, or, find, maximumBy, minimumBy, mapAccumL, mapAccumR, foldl')-import Data.Tuple as Exports-import Data.Function as Exports hiding ((.), id)-import Data.Ord as Exports (Down(..))-import Data.String as Exports-import Data.Int as Exports-import Data.Word as Exports-import Data.Ratio as Exports-import Data.Bits as Exports-import Data.Fixed as Exports-import Data.Ix as Exports-import Data.Data as Exports-import Data.Bool as Exports hiding (bool)-import Text.Read as Exports (readMaybe, readEither)-import Control.Exception as Exports hiding (tryJust, try, assert)-import System.Mem as Exports-import System.Mem.StableName as Exports-import System.Timeout as Exports-import System.Exit as Exports-import System.IO.Unsafe as Exports-import System.IO as Exports (Handle, hClose)-import System.IO.Error as Exports-import Unsafe.Coerce as Exports-import GHC.Conc as Exports-import GHC.Generics as Exports (Generic)-import GHC.IO.Exception as Exports-import GHC.Exts as Exports (lazy, inline)-import Data.IORef as Exports-import Data.STRef as Exports-import Control.Monad.ST as Exports-import Debug.Trace as Exports hiding (traceM)+import BasePrelude as Exports -- placeholders -------------------------@@ -69,6 +29,6 @@ bottom = [e| $bug "Bottom evaluated" |] -bool :: a -> a -> Bool -> a-bool f _ False = f-bool _ t True = t+-- | A replacement for the missing 'Traverse' instance of pair in base < 4.7.+traversePair :: Functor f => (a -> f b) -> (c, a) -> f (c, b)+traversePair f (x, y) = (,) x <$> f y
library/STMContainers/Set.hs view
@@ -6,6 +6,7 @@ insert, delete, lookup,+ focus, foldM, null, )@@ -31,42 +32,57 @@ type ElementKey (HAMTElement e) = e elementKey (HAMTElement e) = e -{-# INLINABLE elementValue #-}+{-# INLINE elementValue #-} elementValue :: HAMTElement e -> e elementValue (HAMTElement e) = e -- | -- Insert a new element.-{-# INLINABLE insert #-}+{-# INLINE insert #-} insert :: (Element e) => e -> Set e -> STM () insert e = HAMT.insert (HAMTElement e) . hamt -- | -- Delete an element.-{-# INLINABLE delete #-}+{-# INLINE delete #-} delete :: (Element e) => e -> Set e -> STM () delete e = HAMT.focus Focus.deleteM e . hamt -- | -- Lookup an element.-{-# INLINABLE lookup #-}+{-# INLINE lookup #-} lookup :: (Element e) => e -> Set e -> STM Bool lookup e = fmap (maybe False (const True)) . HAMT.focus Focus.lookupM e . hamt -- |+-- Focus on an element with a strategy.+-- +-- This function allows to perform simultaneous lookup and modification.+-- +-- The strategy is over a unit since we already know, +-- which element we're focusing on and it doesn't make sense to replace it,+-- however we still can still decide wether to keep or remove it.+{-# INLINE focus #-}+focus :: (Element e) => Focus.StrategyM STM () r -> e -> Set e -> STM r+focus s e = HAMT.focus elementStrategy e . hamt+ where+ elementStrategy = + (fmap . fmap . fmap) (const (HAMTElement e)) . s . fmap (const ())++-- | -- Fold all the elements.-{-# INLINABLE foldM #-}+{-# INLINE foldM #-} foldM :: (a -> e -> STM a) -> a -> Set e -> STM a foldM f a = HAMT.foldM (\a -> f a . elementValue) a . hamt -- | -- Construct a new set.-{-# INLINABLE new #-}+{-# INLINE new #-} new :: STM (Set e) new = Set <$> HAMT.new -- | -- Check, whether the set is empty.-{-# INLINABLE null #-}+{-# INLINE null #-} null :: Set e -> STM Bool null = HAMT.null . hamt
library/STMContainers/SizedArray.hs view
@@ -11,6 +11,11 @@ data SizedArray a = SizedArray {-# UNPACK #-} !Int {-# UNPACK #-} !(Array a) +instance Foldable SizedArray where+ {-# INLINE foldr #-}+ foldr step r (SizedArray size array) =+ foldr step r $ map (indexArray array) [0 .. pred size]+ -- | -- An index of an element. type Index = Int
library/STMContainers/WordArray.hs view
@@ -13,6 +13,11 @@ data WordArray e = WordArray {-# UNPACK #-} !Indices {-# UNPACK #-} !(Array e) +instance Foldable WordArray where+ {-# INLINE foldr #-}+ foldr step r (WordArray indices array) =+ foldr (step . indexArray array) r $ Indices.positions indices+ -- | -- A bitmap of set elements. type Indices = Indices.Indices@@ -178,16 +183,6 @@ {-# INLINE null #-} null :: WordArray e -> Bool null = Indices.null . indices--{-# INLINE traverse_ #-}-traverse_ :: Applicative f => (a -> f b) -> WordArray a -> f ()-traverse_ f =- inline Prelude.traverse_ f . elements--{-# INLINE foldM #-}-foldM :: Monad m => (a -> b -> m a) -> a -> WordArray b -> m a-foldM step acc =- inline Prelude.foldM step acc . elements {-# INLINE focusM #-} focusM :: Monad m => Focus.StrategyM m a r -> Index -> WordArray a -> m (r, Maybe (WordArray a))
library/STMContainers/WordArray/Indices.hs view
@@ -10,10 +10,12 @@ type Index = Int +type Position = Int+ -- | -- A number of indexes, preceding this one. {-# INLINE position #-}-position :: Index -> Indices -> Int+position :: Index -> Indices -> Position position i b = popCount (b .&. (bit i - 1)) {-# INLINE singleton #-}@@ -46,16 +48,21 @@ {-# INLINE fromList #-} fromList :: [Index] -> Indices-fromList = foldr (.|.) 0 . map bit+fromList = Prelude.foldr (.|.) 0 . map bit {-# INLINE toList #-} toList :: Indices -> [Index] toList w = filter (testBit w) allIndices +{-# INLINE positions #-}+positions :: Indices -> [Position]+positions = enumFromTo 0 . pred . size+ {-# NOINLINE allIndices #-} allIndices :: [Index] allIndices = [0 .. pred maxSize] -{-# INLINE traverse_ #-}-traverse_ :: Applicative f => (Index -> f b) -> Indices -> f ()-traverse_ f = inline Prelude.traverse_ f . inline toList+{-# INLINE foldr #-}+foldr :: (Index -> r -> r) -> r -> Indices -> r+foldr s r ix = + Prelude.foldr (\i r' -> if testBit ix i then s i r' else r') r allIndices
stm-containers.cabal view
@@ -1,7 +1,7 @@ name: stm-containers version:- 0.1.2+ 0.1.3 synopsis: Containers for STM description:@@ -51,15 +51,21 @@ STMContainers.HAMT.Nodes STMContainers.HAMT exposed-modules:+ STMContainers.Bimap+ STMContainers.Multimap STMContainers.Map STMContainers.Set build-depends:+ -- data:+ hashable < 1.3,+ -- control:+ focus > 0.1.0 && < 0.2,+ -- preludes:+ base-prelude == 0.1.*, -- debugging: loch-th == 0.2.*, placeholders == 0.1.*, -- general:- focus > 0.1.0 && < 0.2,- hashable < 1.3, primitive == 0.5.*, base >= 4.5 && < 4.8 default-extensions:@@ -77,16 +83,21 @@ main-is: WordArrayTests.hs build-depends:+ -- testing:+ free >= 4.6 && < 4.10,+ mtl == 2.*, QuickCheck == 2.7.*, HTF == 0.11.*,+ -- data:+ hashable < 1.3,+ -- control:+ focus > 0.1.0 && < 0.2,+ -- preludes:+ base-prelude == 0.1.*, -- debugging: loch-th == 0.2.*, placeholders == 0.1.*, -- general:- focus > 0.1.0 && < 0.2,- free >= 4.6 && < 4.10,- mtl == 2.*,- hashable < 1.3, primitive == 0.5.*, base >= 4.5 && < 4.8 default-extensions:@@ -110,6 +121,7 @@ loch-th == 0.2.*, placeholders == 0.1.*, -- general:+ base-prelude == 0.1.*, focus > 0.1.0 && < 0.2, unordered-containers == 0.2.*, free >= 4.6 && < 4.10,@@ -151,7 +163,7 @@ loch-th == 0.2.*, placeholders == 0.1.*, -- general:- base >= 4.5 && < 5+ base >= 4.5 && < 4.8 benchmark concurrent-insertion-bench@@ -183,7 +195,7 @@ -- general: free >= 4.5 && < 4.10, async == 2.0.*,- base >= 4.5 && < 5+ base >= 4.5 && < 4.8 benchmark concurrent-transactions-bench@@ -217,4 +229,4 @@ mtl == 2.*, free >= 4.5 && < 4.10, async == 2.0.*,- base >= 4.5 && < 5+ base >= 4.5 && < 4.8