vector-hashtables 0.1.1.4 → 0.1.2.0
raw patch · 12 files changed
+494/−194 lines, 12 filesdep ~QuickCheckPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependency ranges changed: QuickCheck
API changes (from Hackage documentation)
- Data.Vector.Hashtables.Internal: getPrime :: Int -> Int
- Data.Vector.Hashtables.Internal: primes :: Vector Int
+ Data.Vector.Hashtables: [fremSize] :: FrozenDictionary ks k vs v -> {-# UNPACK #-} !FastRem
+ Data.Vector.Hashtables: [remSize] :: Dictionary_ s ks k vs v -> {-# UNPACK #-} !FastRem
+ Data.Vector.Hashtables: findEntry_ :: (MVector ks k, MVector vs v, PrimMonad m, Hashable k, Eq k) => Dictionary_ (PrimState m) ks k vs v -> k -> m Int
+ Data.Vector.Hashtables: upsert :: (MVector ks k, MVector vs v, PrimMonad m, Hashable k, Eq k) => Dictionary (PrimState m) ks k vs v -> (Maybe v -> v) -> k -> m ()
+ Data.Vector.Hashtables.Internal: FastRem :: !Int -> !Int -> !Int -> FastRem
+ Data.Vector.Hashtables.Internal: [_frmMulHi] :: FastRem -> !Int
+ Data.Vector.Hashtables.Internal: [_frmShift] :: FastRem -> !Int
+ Data.Vector.Hashtables.Internal: [fremSize] :: FrozenDictionary ks k vs v -> {-# UNPACK #-} !FastRem
+ Data.Vector.Hashtables.Internal: [frmPrime] :: FastRem -> !Int
+ Data.Vector.Hashtables.Internal: [remSize] :: Dictionary_ s ks k vs v -> {-# UNPACK #-} !FastRem
+ Data.Vector.Hashtables.Internal: data FastRem
+ Data.Vector.Hashtables.Internal: fastRem :: Int -> FastRem -> Int
+ Data.Vector.Hashtables.Internal: findEntry_ :: (MVector ks k, MVector vs v, PrimMonad m, Hashable k, Eq k) => Dictionary_ (PrimState m) ks k vs v -> k -> m Int
+ Data.Vector.Hashtables.Internal: getFastRem :: Int -> FastRem
+ Data.Vector.Hashtables.Internal: instance GHC.Classes.Eq Data.Vector.Hashtables.Internal.FastRem
+ Data.Vector.Hashtables.Internal: instance GHC.Classes.Ord Data.Vector.Hashtables.Internal.FastRem
+ Data.Vector.Hashtables.Internal: instance GHC.Show.Show Data.Vector.Hashtables.Internal.FastRem
+ Data.Vector.Hashtables.Internal: primesWithFastRem :: Vector (Int, Int, Int)
+ Data.Vector.Hashtables.Internal: upsert :: (MVector ks k, MVector vs v, PrimMonad m, Hashable k, Eq k) => Dictionary (PrimState m) ks k vs v -> (Maybe v -> v) -> k -> m ()
- Data.Primitive.PrimArray.Utils: length :: Prim a => MutablePrimArray s a -> Int
+ Data.Primitive.PrimArray.Utils: length :: (PrimMonad m, Prim a) => MutablePrimArray (PrimState m) a -> m Int
- Data.Vector.Hashtables: Dictionary :: IntArray s -> ks s k -> vs s v -> Dictionary_ s ks k vs v
+ Data.Vector.Hashtables: Dictionary :: !IntArray s -> !ks s k -> !vs s v -> {-# UNPACK #-} !FastRem -> Dictionary_ s ks k vs v
- Data.Vector.Hashtables: FrozenDictionary :: PrimArray Int -> Int -> ks k -> vs v -> FrozenDictionary ks k vs v
+ Data.Vector.Hashtables: FrozenDictionary :: !PrimArray Int -> !Int -> !ks k -> !vs v -> {-# UNPACK #-} !FastRem -> FrozenDictionary ks k vs v
- Data.Vector.Hashtables: [count, freeList, freeCount] :: FrozenDictionary ks k vs v -> Int
+ Data.Vector.Hashtables: [count, freeList, freeCount] :: FrozenDictionary ks k vs v -> !Int
- Data.Vector.Hashtables: [fhashCode, fnext, fbuckets] :: FrozenDictionary ks k vs v -> PrimArray Int
+ Data.Vector.Hashtables: [fhashCode, fnext, fbuckets] :: FrozenDictionary ks k vs v -> !PrimArray Int
- Data.Vector.Hashtables: [fkey] :: FrozenDictionary ks k vs v -> ks k
+ Data.Vector.Hashtables: [fkey] :: FrozenDictionary ks k vs v -> !ks k
- Data.Vector.Hashtables: [fvalue] :: FrozenDictionary ks k vs v -> vs v
+ Data.Vector.Hashtables: [fvalue] :: FrozenDictionary ks k vs v -> !vs v
- Data.Vector.Hashtables: [hashCode, next, buckets, refs] :: Dictionary_ s ks k vs v -> IntArray s
+ Data.Vector.Hashtables: [hashCode, next, buckets, refs] :: Dictionary_ s ks k vs v -> !IntArray s
- Data.Vector.Hashtables: [key] :: Dictionary_ s ks k vs v -> ks s k
+ Data.Vector.Hashtables: [key] :: Dictionary_ s ks k vs v -> !ks s k
- Data.Vector.Hashtables: [value] :: Dictionary_ s ks k vs v -> vs s v
+ Data.Vector.Hashtables: [value] :: Dictionary_ s ks k vs v -> !vs s v
- Data.Vector.Hashtables.Internal: Dictionary :: IntArray s -> ks s k -> vs s v -> Dictionary_ s ks k vs v
+ Data.Vector.Hashtables.Internal: Dictionary :: !IntArray s -> !ks s k -> !vs s v -> {-# UNPACK #-} !FastRem -> Dictionary_ s ks k vs v
- Data.Vector.Hashtables.Internal: FrozenDictionary :: PrimArray Int -> Int -> ks k -> vs v -> FrozenDictionary ks k vs v
+ Data.Vector.Hashtables.Internal: FrozenDictionary :: !PrimArray Int -> !Int -> !ks k -> !vs v -> {-# UNPACK #-} !FastRem -> FrozenDictionary ks k vs v
- Data.Vector.Hashtables.Internal: [count, freeList, freeCount] :: FrozenDictionary ks k vs v -> Int
+ Data.Vector.Hashtables.Internal: [count, freeList, freeCount] :: FrozenDictionary ks k vs v -> !Int
- Data.Vector.Hashtables.Internal: [fhashCode, fnext, fbuckets] :: FrozenDictionary ks k vs v -> PrimArray Int
+ Data.Vector.Hashtables.Internal: [fhashCode, fnext, fbuckets] :: FrozenDictionary ks k vs v -> !PrimArray Int
- Data.Vector.Hashtables.Internal: [fkey] :: FrozenDictionary ks k vs v -> ks k
+ Data.Vector.Hashtables.Internal: [fkey] :: FrozenDictionary ks k vs v -> !ks k
- Data.Vector.Hashtables.Internal: [fvalue] :: FrozenDictionary ks k vs v -> vs v
+ Data.Vector.Hashtables.Internal: [fvalue] :: FrozenDictionary ks k vs v -> !vs v
- Data.Vector.Hashtables.Internal: [hashCode, next, buckets, refs] :: Dictionary_ s ks k vs v -> IntArray s
+ Data.Vector.Hashtables.Internal: [hashCode, next, buckets, refs] :: Dictionary_ s ks k vs v -> !IntArray s
- Data.Vector.Hashtables.Internal: [key] :: Dictionary_ s ks k vs v -> ks s k
+ Data.Vector.Hashtables.Internal: [key] :: Dictionary_ s ks k vs v -> !ks s k
- Data.Vector.Hashtables.Internal: [value] :: Dictionary_ s ks k vs v -> vs s v
+ Data.Vector.Hashtables.Internal: [value] :: Dictionary_ s ks k vs v -> !vs s v
- Data.Vector.Hashtables.Internal: resize :: forall m (v1 :: Type -> Type -> Type) a1 (v2 :: Type -> Type -> Type) a2. (PrimMonad m, MVector v1 a1, MVector v2 a2) => Dictionary_ (PrimState m) v1 a1 v2 a2 -> Int -> Int -> a1 -> a2 -> m (Dictionary_ (PrimState m) v1 a1 v2 a2)
+ Data.Vector.Hashtables.Internal: resize :: (MVector ks k, MVector vs v, PrimMonad m, Hashable k, Eq k) => Dictionary_ (PrimState m) ks k vs v -> Int -> Int -> k -> v -> m (Dictionary_ (PrimState m) ks k vs v)
Files
- README.md +170/−89
- bench/Main.hs +7/−0
- changelog.md +11/−0
- gen/GenPrimes.hs +45/−0
- src-gen/Data/Vector/Hashtables/Internal/Mask.hs +0/−11
- src-i386/Data/Vector/Hashtables/Internal/Mask.hs +0/−11
- src/Data/Primitive/PrimArray/Utils.hs +12/−6
- src/Data/Vector/Hashtables.hs +6/−2
- src/Data/Vector/Hashtables/Internal.hs +197/−64
- src/Data/Vector/Hashtables/Internal/Mask.hs +11/−0
- test/Data/Vector/HashTablesSpec.hs +26/−0
- vector-hashtables.cabal +9/−11
README.md view
@@ -12,7 +12,7 @@ (and `vector` where relevant) -Benchmarks below are produced under GHC 9.2.5, and can be reproduced locally with+Benchmarks below are produced under GHC 9.4.8, and can be reproduced locally with ```shellsession cabal bench --benchmark-options="--csv results.csv"@@ -21,91 +21,172 @@ (You will need the `bench-show` tool, which is available from Hackage.) -```-Benchmark default(ns)------------------------------------------------------------------ -------------Comparison/1000/insert/hashtables basic 47112.15-Comparison/1000/insert/vector-hashtables boxed 22941.55-Comparison/1000/insert/vector-hashtables unboxed keys 22338.54-Comparison/1000/insert/vector-hashtables 19460.33-Comparison/1000/insert/mutable vector boxed 3216.07-Comparison/1000/insert/mutable vector 659.46-Comparison/1000/insert (resize)/hashtables basic 158026.86-Comparison/1000/insert (resize)/vector-hashtables boxed 42164.21-Comparison/1000/insert (resize)/vector-hashtables unboxed keys 40334.65-Comparison/1000/insert (resize)/vector-hashtables 37761.08-Comparison/1000/insert, delete/hashtables basic 85218.96-Comparison/1000/insert, delete/vector-hashtables 36199.84-Comparison/1000/find/hashtables basic 33857.19-Comparison/1000/find/vector-hashtables 14205.24-Comparison/1000/find/vector-hashtables (frozen) 12257.88-Comparison/1000/lookupIndex/hashtables basic 31842.70-Comparison/1000/lookupIndex/vector-hashtables 14140.17-Comparison/1000/fromList/hashtables basic 161048.28-Comparison/1000/fromList/vector-hashtables 48052.28-Comparison/1000/toList/hashtables basic 9569.82-Comparison/1000/toList/vector-hashtables 12600.58-Comparison/10000/insert/hashtables basic 983062.29-Comparison/10000/insert/vector-hashtables boxed 225218.39-Comparison/10000/insert/vector-hashtables unboxed keys 212758.35-Comparison/10000/insert/vector-hashtables 186573.02-Comparison/10000/insert/mutable vector boxed 37415.65-Comparison/10000/insert/mutable vector 6956.86-Comparison/10000/insert (resize)/hashtables basic 1696364.18-Comparison/10000/insert (resize)/vector-hashtables boxed 479122.17-Comparison/10000/insert (resize)/vector-hashtables unboxed keys 422759.56-Comparison/10000/insert (resize)/vector-hashtables 352441.24-Comparison/10000/insert, delete/hashtables basic 1426337.13-Comparison/10000/insert, delete/vector-hashtables 359228.91-Comparison/10000/find/hashtables basic 330890.67-Comparison/10000/find/vector-hashtables 141525.60-Comparison/10000/find/vector-hashtables (frozen) 122542.86-Comparison/10000/lookupIndex/hashtables basic 327065.33-Comparison/10000/lookupIndex/vector-hashtables 142678.55-Comparison/10000/fromList/hashtables basic 1641384.65-Comparison/10000/fromList/vector-hashtables 542530.31-Comparison/10000/toList/hashtables basic 143188.55-Comparison/10000/toList/vector-hashtables 274318.45-Comparison/100000/insert/hashtables basic 10907947.88-Comparison/100000/insert/vector-hashtables boxed 6932748.81-Comparison/100000/insert/vector-hashtables unboxed keys 5307166.72-Comparison/100000/insert/vector-hashtables 2368482.76-Comparison/100000/insert/mutable vector boxed 1794351.95-Comparison/100000/insert/mutable vector 69989.51-Comparison/100000/insert (resize)/hashtables basic 22232168.04-Comparison/100000/insert (resize)/vector-hashtables boxed 11420001.24-Comparison/100000/insert (resize)/vector-hashtables unboxed keys 9496935.97-Comparison/100000/insert (resize)/vector-hashtables 5988321.89-Comparison/100000/insert, delete/hashtables basic 15119684.92-Comparison/100000/insert, delete/vector-hashtables 4058662.48-Comparison/100000/find/hashtables basic 3473800.70-Comparison/100000/find/vector-hashtables 1431873.00-Comparison/100000/find/vector-hashtables (frozen) 1225155.68-Comparison/100000/lookupIndex/hashtables basic 3235041.49-Comparison/100000/lookupIndex/vector-hashtables 1439338.81-Comparison/100000/fromList/hashtables basic 22917643.59-Comparison/100000/fromList/vector-hashtables 8603353.43-Comparison/100000/toList/hashtables basic 5336633.37-Comparison/100000/toList/vector-hashtables 9377042.71-Comparison/1000000/insert/hashtables basic 109193301.77-Comparison/1000000/insert/vector-hashtables boxed 77176586.83-Comparison/1000000/insert/vector-hashtables unboxed keys 59037764.29-Comparison/1000000/insert/vector-hashtables 28535981.51-Comparison/1000000/insert/mutable vector boxed 29709444.79-Comparison/1000000/insert/mutable vector 866220.55-Comparison/1000000/insert (resize)/hashtables basic 260585504.79-Comparison/1000000/insert (resize)/vector-hashtables boxed 127608418.79-Comparison/1000000/insert (resize)/vector-hashtables unboxed keys 96059752.01-Comparison/1000000/insert (resize)/vector-hashtables 57038345.24-Comparison/1000000/insert, delete/hashtables basic 153182769.34-Comparison/1000000/insert, delete/vector-hashtables 47664402.90-Comparison/1000000/find/hashtables basic 35821229.20-Comparison/1000000/find/vector-hashtables 15711648.56-Comparison/1000000/find/vector-hashtables (frozen) 12885372.25-Comparison/1000000/lookupIndex/hashtables basic 32210529.62-Comparison/1000000/lookupIndex/vector-hashtables 14911308.87-Comparison/1000000/fromList/hashtables basic 446988819.28-Comparison/1000000/fromList/vector-hashtables 92793579.75-Comparison/1000000/toList/hashtables basic 66679253.65-Comparison/1000000/toList/vector-hashtables 105470198.31-```+| Benchmark | v.0.1.1.4 (ns) | current (ns) |+| ----------------------------------------------------------------- | ------------ | ------------ | +| Comparison/1000/insert/hashtables basic | 36083.57 | 36907.68 |+| Comparison/1000/insert/vector-hashtables boxed | 25735.13 | 17055.86 |+| Comparison/1000/insert/vector-hashtables unboxed keys | 23601.08 | 15333.05 |+| Comparison/1000/insert/vector-hashtables | 25298.67 | 13881.72 |+| Comparison/1000/insert/mutable vector boxed | 6458.14 | 3658.16 |+| Comparison/1000/insert/mutable vector | 2526.35 | 1282.13 |+| Comparison/1000/insert (resize)/hashtables basic | 318207.51 | 159834.87 |+| Comparison/1000/insert (resize)/vector-hashtables boxed | 106490.84 | 25900.28 |+| Comparison/1000/insert (resize)/vector-hashtables unboxed keys | 66848.40 | 25382.56 |+| Comparison/1000/insert (resize)/vector-hashtables | 65694.66 | 21393.31 |+| Comparison/1000/insert, delete/hashtables basic | 96776.41 | 72615.51 |+| Comparison/1000/insert, delete/vector-hashtables | 79701.44 | 20920.26 |+| Comparison/1000/find/hashtables basic | 22841.85 | 23678.93 |+| Comparison/1000/find/vector-hashtables | 16398.19 | 5586.93 |+| Comparison/1000/find/vector-hashtables (frozen) | 11967.63 | 3128.18 |+| Comparison/1000/lookupIndex/hashtables basic | 19744.27 | 19209.79 |+| Comparison/1000/lookupIndex/vector-hashtables | 13624.09 | 4978.75 |+| Comparison/1000/fromList/hashtables basic | 167277.66 | 162920.75 |+| Comparison/1000/fromList/vector-hashtables | 48670.75 | 25283.17 |+| Comparison/1000/toList/hashtables basic | 9296.91 | 9286.65 |+| Comparison/1000/toList/vector-hashtables | 9753.58 | 10022.51 |+| Comparison/10000/insert/hashtables basic | 384031.02 | 373885.01 |+| Comparison/10000/insert/vector-hashtables boxed | 246923.90 | 175892.73 |+| Comparison/10000/insert/vector-hashtables unboxed keys | 229812.05 | 147689.46 |+| Comparison/10000/insert/vector-hashtables | 216924.38 | 128417.71 |+| Comparison/10000/insert/mutable vector boxed | 43104.20 | 42712.36 |+| Comparison/10000/insert/mutable vector | 12298.81 | 12193.85 |+| Comparison/10000/insert (resize)/hashtables basic | 1342541.64 | 1378595.70 |+| Comparison/10000/insert (resize)/vector-hashtables boxed | 487188.49 | 273187.43 |+| Comparison/10000/insert (resize)/vector-hashtables unboxed keys | 441635.60 | 242659.06 |+| Comparison/10000/insert (resize)/vector-hashtables | 412651.12 | 188011.22 |+| Comparison/10000/insert, delete/hashtables basic | 722883.16 | 736250.66 |+| Comparison/10000/insert, delete/vector-hashtables | 407113.19 | 200643.92 |+| Comparison/10000/find/hashtables basic | 228154.09 | 232874.24 |+| Comparison/10000/find/vector-hashtables | 164343.06 | 55693.39 |+| Comparison/10000/find/vector-hashtables (frozen) | 119669.00 | 31291.02 |+| Comparison/10000/lookupIndex/hashtables basic | 197212.48 | 191707.57 |+| Comparison/10000/lookupIndex/vector-hashtables | 136205.70 | 49762.50 |+| Comparison/10000/fromList/hashtables basic | 1391968.88 | 1562065.86 |+| Comparison/10000/fromList/vector-hashtables | 430590.93 | 233128.12 |+| Comparison/10000/toList/hashtables basic | 112894.13 | 114543.83 |+| Comparison/10000/toList/vector-hashtables | 152214.13 | 154034.89 |+| Comparison/100000/insert/hashtables basic | 4492224.86 | 4546924.57 |+| Comparison/100000/insert/vector-hashtables boxed | 2414664.98 | 1767261.44 |+| Comparison/100000/insert/vector-hashtables unboxed keys | 2243027.45 | 1476174.02 |+| Comparison/100000/insert/vector-hashtables | 2144269.70 | 1272101.52 |+| Comparison/100000/insert/mutable vector boxed | 667945.33 | 666673.13 |+| Comparison/100000/insert/mutable vector | 121264.87 | 123138.53 |+| Comparison/100000/insert (resize)/hashtables basic | 18217163.16 | 18777839.68 |+| Comparison/100000/insert (resize)/vector-hashtables boxed | 9452674.69 | 7287443.14 |+| Comparison/100000/insert (resize)/vector-hashtables unboxed keys | 7721635.33 | 5698107.38 |+| Comparison/100000/insert (resize)/vector-hashtables | 4722092.98 | 2574932.04 |+| Comparison/100000/insert, delete/hashtables basic | 8699786.66 | 8790937.73 |+| Comparison/100000/insert, delete/vector-hashtables | 4073127.47 | 2010559.30 |+| Comparison/100000/find/hashtables basic | 2283995.01 | 2346364.66 |+| Comparison/100000/find/vector-hashtables | 1676135.49 | 588427.30 |+| Comparison/100000/find/vector-hashtables (frozen) | 1201572.29 | 319639.97 |+| Comparison/100000/lookupIndex/hashtables basic | 1963727.73 | 1931036.13 |+| Comparison/100000/lookupIndex/vector-hashtables | 1363501.76 | 499992.98 |+| Comparison/100000/fromList/hashtables basic | 20681183.31 | 30059346.94 |+| Comparison/100000/fromList/vector-hashtables | 5262183.79 | 3945839.47 |+| Comparison/100000/toList/hashtables basic | 2675794.96 | 2702739.12 |+| Comparison/100000/toList/vector-hashtables | 5155629.15 | 5118781.70 |+| Comparison/1000000/insert/hashtables basic | 86723317.72 | 85752701.43 |+| Comparison/1000000/insert/vector-hashtables boxed | 68162021.23 | 75667649.90 |+| Comparison/1000000/insert/vector-hashtables unboxed keys | 50777620.44 | 46615543.58 |+| Comparison/1000000/insert/vector-hashtables | 23334885.43 | 16025927.63 |+| Comparison/1000000/insert/mutable vector boxed | 30281652.62 | 32068295.04 |+| Comparison/1000000/insert/mutable vector | 1283399.43 | 1393859.51 |+| Comparison/1000000/insert (resize)/hashtables basic | 228726522.46 | 282346897.14 |+| Comparison/1000000/insert (resize)/vector-hashtables boxed | 104556190.01 | 84385042.40 |+| Comparison/1000000/insert (resize)/vector-hashtables unboxed keys | 79183320.30 | 62413398.66 |+| Comparison/1000000/insert (resize)/vector-hashtables | 45925222.08 | 28777902.04 |+| Comparison/1000000/insert, delete/hashtables basic | 130189177.30 | 134399640.44 |+| Comparison/1000000/insert, delete/vector-hashtables | 42722592.04 | 23648387.28 |+| Comparison/1000000/find/hashtables basic | 23094297.73 | 24583079.42 |+| Comparison/1000000/find/vector-hashtables | 16709242.48 | 6178348.57 |+| Comparison/1000000/find/vector-hashtables (frozen) | 12176361.82 | 3425505.60 |+| Comparison/1000000/lookupIndex/hashtables basic | 20222788.08 | 19753759.16 |+| Comparison/1000000/lookupIndex/vector-hashtables | 14041315.59 | 5357116.98 |+| Comparison/1000000/fromList/hashtables basic | 210947448.60 | 222974094.62 |+| Comparison/1000000/fromList/vector-hashtables | 56875691.60 | 49212505.34 |+| Comparison/1000000/toList/hashtables basic | 62256321.15 | 66351583.99 |+| Comparison/1000000/toList/vector-hashtables | 95883670.57 | 98441804.39 |++<details><summary>Utilities benchmark:</summary>++| Benchmark | v.0.1.1.4 (ns) | current (ns) |+| --------- | -------------- | ------------ |+| Utilities/1000/at' | 14554.08 | 4755.41 |+| Utilities/1000/insert | 16704.55 | 6842.45 |+| Utilities/1000/delete | 11166.80 | 3959.44 |+| Utilities/1000/lookup | 14510.59 | 5161.87 |+| Utilities/1000/lookup' | 14181.87 | 4739.19 |+| Utilities/1000/lookupIndex | 17171.76 | 4196.40 |+| Utilities/1000/null | 7.19 | 7.92 |+| Utilities/1000/length | 7.31 | 7.41 |+| Utilities/1000/size | 6.85 | 6.53 |+| Utilities/1000/member | 17364.48 | 4344.27 |+| Utilities/1000/findWithDefault | 15747.10 | 4802.97 |+| Utilities/1000/upsert | 32633.25 | 12708.79 |+| Utilities/1000/alter | 31908.54 | 11109.48 |+| Utilities/1000/alterM | 32564.14 | 11110.53 |+| Utilities/1000/union | 46432.10 | 29468.82 |+| Utilities/1000/difference | 25741.68 | 16296.53 |+| Utilities/1000/intersection | 58828.38 | 38587.57 |+| Utilities/1000/fromList | 45355.85 | 26156.93 |+| Utilities/1000/toList | 9626.89 | 10101.05 |+| Utilities/10000/at' | 147509.66 | 48699.88 |+| Utilities/10000/insert | 171201.56 | 68568.34 |+| Utilities/10000/delete | 111650.42 | 39392.43 |+| Utilities/10000/lookup | 149138.47 | 49800.41 |+| Utilities/10000/lookup' | 144283.31 | 46886.69 |+| Utilities/10000/lookupIndex | 172630.09 | 40088.94 |+| Utilities/10000/null | 7.20 | 7.24 |+| Utilities/10000/length | 7.30 | 6.77 |+| Utilities/10000/size | 6.87 | 6.43 |+| Utilities/10000/member | 170650.68 | 43369.07 |+| Utilities/10000/findWithDefault | 157236.92 | 49471.03 |+| Utilities/10000/upsert | 329212.06 | 125290.78 |+| Utilities/10000/alter | 322814.62 | 111817.84 |+| Utilities/10000/alterM | 330094.30 | 112444.63 |+| Utilities/10000/union | 478541.46 | 329790.79 |+| Utilities/10000/difference | 295042.17 | 193790.26 |+| Utilities/10000/intersection | 644396.71 | 419483.32 |+| Utilities/10000/fromList | 494164.34 | 331449.21 |+| Utilities/10000/toList | 151375.79 | 167580.99 |+| Utilities/100000/at' | 1491045.70 | 495418.68 |+| Utilities/100000/insert | 1741058.94 | 765507.51 |+| Utilities/100000/delete | 1127146.84 | 436707.82 |+| Utilities/100000/lookup | 1601916.69 | 562205.51 |+| Utilities/100000/lookup' | 1441526.57 | 488540.28 |+| Utilities/100000/lookupIndex | 1763172.42 | 405596.28 |+| Utilities/100000/null | 7.19 | 7.26 |+| Utilities/100000/length | 7.38 | 7.17 |+| Utilities/100000/size | 6.92 | 6.83 |+| Utilities/100000/member | 1740066.09 | 464281.90 |+| Utilities/100000/findWithDefault | 1577458.36 | 489790.85 |+| Utilities/100000/upsert | 3383104.75 | 1265454.18 |+| Utilities/100000/alter | 3329820.09 | 1211692.27 |+| Utilities/100000/alterM | 3356140.57 | 1220060.24 |+| Utilities/100000/union | 5563999.76 | 3705665.39 |+| Utilities/100000/difference | 6372930.19 | 5630405.29 |+| Utilities/100000/intersection | 12353680.59 | 9595098.36 |+| Utilities/100000/fromList | 5161712.37 | 3685646.90 |+| Utilities/100000/toList | 5109243.49 | 5118785.28 |+| Utilities/1000000/at' | 14831244.23 | 5055419.26 |+| Utilities/1000000/insert | 17633535.06 | 7209602.12 |+| Utilities/1000000/delete | 11251853.98 | 4072535.57 |+| Utilities/1000000/lookup | 15169518.90 | 5208497.64 |+| Utilities/1000000/lookup' | 14532451.21 | 4929673.79 |+| Utilities/1000000/lookupIndex | 17529914.96 | 4216663.23 |+| Utilities/1000000/null | 7.20 | 7.25 |+| Utilities/1000000/length | 7.30 | 6.81 |+| Utilities/1000000/size | 6.86 | 6.43 |+| Utilities/1000000/member | 17461069.35 | 4604944.15 |+| Utilities/1000000/findWithDefault | 15945541.78 | 5058608.89 |+| Utilities/1000000/upsert | 34444162.79 | 12052143.01 |+| Utilities/1000000/alter | 33820504.88 | 11842773.92 |+| Utilities/1000000/alterM | 33991841.71 | 11908234.12 |+| Utilities/1000000/union | 59911378.73 | 44470700.12 |+| Utilities/1000000/difference | 117323371.53 | 107670945.26 |+| Utilities/1000000/intersection | 195009586.71 | 161847790.47 |+| Utilities/1000000/fromList | 97086662.77 | 52734408.34 |+| Utilities/1000000/toList | 166554860.99 | 99619875.06 |++</details>
bench/Main.hs view
@@ -241,6 +241,7 @@ hSize <- vh n hMember <- vh n hFindWithDefault <- vh n+ hUpsert <- vh n hAlter <- vh n hAlterM <- vh n hUnion1 <- vh n@@ -264,6 +265,7 @@ , bench "size" $ nfIO (bhusize hSize) , bench "member" $ nfIO (bhumember n hMember) , bench "findWithDefault" $ nfIO (bhufindWithDefault n hFindWithDefault)+ , bench "upsert" $ nfIO (bhuupsert n hUpsert) , bench "alter" $ nfIO (bhualter n hAlter) , bench "alterM" $ nfIO (bhualterM n hAlterM) , bench "union" $ nfIO (bhuunion hUnion1 hUnion2)@@ -315,6 +317,11 @@ bhufindWithDefault n ht = do let go !i | i <= n = VH.findWithDefault ht 0 i >> go (i + 1)+ | otherwise = return ()+ go 0++bhuupsert n ht = do+ let go !i | i <= n = VH.upsert ht (maybe minBound succ) i >> go (i + 1) | otherwise = return () go 0
changelog.md view
@@ -1,3 +1,14 @@+# 0.1.2.0 (2024-04-26)++* Add `upsert` function to public interface (see [#21](https://github.com/klapaucius/vector-hashtables/pull/21)).+* Simplify support of 32-bit architectures via `maxBound` (see [#22](https://github.com/klapaucius/vector-hashtables/pull/22)).+* Improve performance via strictness annotations and bang patterns (see [#24](https://github.com/klapaucius/vector-hashtables/pull/24)).+* Suggest using strict boxed vectors to avoid accumulation of thunks (see [#27](https://github.com/klapaucius/vector-hashtables/pull/27)).+* Speed up division by bucket's size (see [#28](https://github.com/klapaucius/vector-hashtables/pull/28)).+* Avoid deprecated `sizeofMutablePrimArray` (see [#29](https://github.com/klapaucius/vector-hashtables/pull/29)).+* Introduce `findEntry_` and avoid examining `MutVar` twice in `at` (see [#32](https://github.com/klapaucius/vector-hashtables/pull/32)).+* Bump `QuickCheck` boundary (see [#33](https://github.com/klapaucius/vector-hashtables/pull/33)).+ # 0.1.1.4 (2023-12-13) * Add `wasm32` support (see [#20](https://github.com/klapaucius/vector-hashtables/pull/20)).
+ gen/GenPrimes.hs view
@@ -0,0 +1,45 @@+#!/usr/bin/env cabal+{- cabal:+build-depends: arithmoi, base, quote-quot+-}++import Data.Bits (FiniteBits)+import Data.Int (Int32, Int64)+import Math.NumberTheory.Primes (nextPrime, unPrime)+import Numeric.QuoteQuot (AST (..), assumeNonNegArg, astQuot)++-- | For a given bitness, expressed as a ~ Int32 or a ~ Int64,+-- generate a list of primes such that each prime is at least 20% larger+-- than the previous. Additionally, for each of selected primes+-- there exist numbers m and s such that (assuming Int64) for every n >= 0+--+-- n `quot` p = (n * m) `shiftR` (64 + s)+--+-- The function returns a list of tuples (p, m, s).+--+genPrimes :: (FiniteBits a, Integral a, Show a, Bounded a) => [(a, a, Int)]+genPrimes = go 3+ where+ go n+ | n < 0 = []+ | n >= maxBound `quot` 2 = []+ | p < n = []+ | otherwise = case assumeNonNegArg (astQuot p) of+ Shr (MulHi Arg mul) shft -> (p, mul, shft) : go p'+ _ -> go (p + 1)+ where+ p = fromInteger (unPrime (nextPrime (toInteger n)))+ p' = ceiling (fromIntegral p * 1.2 :: Double)++main :: IO ()+main = do+ putStrLn "-- | This data is auto-generated by GenPrimes.hs."+ putStrLn "-- The vector contains tuples (p, m, s) such that p is prime"+ putStrLn "-- and (assuming 64-bit architecture) for every n >= 0"+ putStrLn "-- it holds that n `quot` p = (n * m) `shiftR` (64 + s),"+ putStrLn "-- enabling faster computation of remainders."+ putStrLn "primesWithFastRem :: UI.Vector (Int, Int, Int)"+ putStrLn "primesWithFastRem = UI.fromList $"+ putStrLn " if finiteBitSize (0 :: Int) == 32"+ putStrLn $ " then " ++ show (genPrimes :: [(Int32, Int32, Int)])+ putStrLn $ " else " ++ show (genPrimes :: [(Int64, Int64, Int)])
− src-gen/Data/Vector/Hashtables/Internal/Mask.hs
@@ -1,11 +0,0 @@-{-|-Module : Data.Vector.Hashtables.Internal.Mask-Description : Provides arch-dependent mask for hashtables.-Copyright : (c) klapaucius, swamp_agr, 2016-2021-License : BSD3--}-module Data.Vector.Hashtables.Internal.Mask where---- | 'Int' mask. For 32-bit it is equal to @0x7FFFFFFF@. Otherwise, @0x7FFFFFFFFFFFFFFF@.-mask = 0x7FFFFFFFFFFFFFFF :: Int-{-# INLINE mask #-}
− src-i386/Data/Vector/Hashtables/Internal/Mask.hs
@@ -1,11 +0,0 @@-{-|-Module : Data.Vector.Hashtables.Internal.Mask-Description : Provides arch-dependent mask for hashtables.-Copyright : (c) klapaucius, swamp_agr, 2016-2021-License : BSD3--}-module Data.Vector.Hashtables.Internal.Mask where---- | 'Int' mask. For 32-bit it is equal to @0x7FFFFFFF@. Otherwise, @0x7FFFFFFFFFFFFFFF@.-mask = 0x7FFFFFFF :: Int-{-# INLINE mask #-}
src/Data/Primitive/PrimArray/Utils.hs view
@@ -14,14 +14,17 @@ => Int -> a -> m (MutablePrimArray (PrimState m) a) replicate n x = do xs <- newPrimArray n- setPrimArray xs 0 (sizeofMutablePrimArray xs) x+ sz <- getSizeofMutablePrimArray xs+ setPrimArray xs 0 sz x return xs {-# INLINE replicate #-} clone :: (PrimMonad m, Prim a) => MutablePrimArray (PrimState m) a -> m (MutablePrimArray (PrimState m) a)-clone xs = cloneMutablePrimArray xs 0 (sizeofMutablePrimArray xs)+clone xs = do+ sz <- getSizeofMutablePrimArray xs+ cloneMutablePrimArray xs 0 sz {-# INLINE clone #-} @@ -41,14 +44,17 @@ => a -> MutablePrimArray (PrimState m) a -> Int -> m (MutablePrimArray (PrimState m) a) growWith a xs delta = do r <- growNoZ xs delta- setPrimArray r (sizeofMutablePrimArray xs) delta a+ sz <- getSizeofMutablePrimArray xs+ setPrimArray r sz delta a return r {-# INLINE growWith #-} growNoZ :: (PrimMonad m, Prim a) => MutablePrimArray (PrimState m) a -> Int -> m (MutablePrimArray (PrimState m) a)-growNoZ xs delta = resizeMutablePrimArray xs (sizeofMutablePrimArray xs + delta)+growNoZ xs delta = do+ sz <- getSizeofMutablePrimArray xs+ resizeMutablePrimArray xs (sz + delta) {-# INLINE growNoZ #-} @@ -60,7 +66,7 @@ {-# INLINE freeze #-} -length :: Prim a => MutablePrimArray s a -> Int-length = sizeofMutablePrimArray+length :: (PrimMonad m, Prim a) => MutablePrimArray (PrimState m) a -> m Int+length = getSizeofMutablePrimArray {-# INLINE length #-}
src/Data/Vector/Hashtables.hs view
@@ -15,8 +15,6 @@ Dictionary (..) , FrozenDictionary (..) , findElem- , Dictionary_ (..)- , findEntry -- ** Construction , initialize@@ -31,8 +29,10 @@ , lookup' , insert , delete+ , upsert , alter , alterM+ , findEntry -- ** Combine @@ -59,6 +59,10 @@ -- *** List , fromList , toList++ -- ** Low-level interface+ , Dictionary_ (..)+ , findEntry_ , module Control.Monad.Primitive ) where
src/Data/Vector/Hashtables/Internal.hs view
@@ -5,9 +5,12 @@ License : BSD3 -} {-# LANGUAGE BangPatterns #-}+{-# LANGUAGE CPP #-} {-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE MagicHash #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE UnboxedTuples #-} module Data.Vector.Hashtables.Internal where import Control.Monad@@ -33,25 +36,26 @@ import Data.Vector.Hashtables.Internal.Mask (mask) --- | Alias for 'MutablePrimArray s Int'.+-- | Alias for 'MutablePrimArray' @s@ 'Int'. type IntArray s = A.MutablePrimArray s Int --- | Single-element mutable array of 'Dictionary_' with primitive state token parameterized with state, keys and values types.+-- | Single-element mutable array of 'Dictionary_' with primitive state token+-- parameterized with state, keys and values types. ----- *Example*:+-- Different flavors of 'MVector' could be used for keys and values.+-- It's preferable to use "Data.Vector.Unboxed.Mutable"+-- or "Data.Vector.Storable.Mutable" if possible. Otherwise,+-- if you must use boxed vectors, consider employing strict ones from+-- [@strict-containers@](https://hackage.haskell.org/package/strict-containers)+-- to eliminate potential accumulation of thunks. --+-- ==== Example+-- -- >>> import qualified Data.Vector.Storable.Mutable as VM -- >>> import qualified Data.Vector.Unboxed.Mutable as UM -- >>> import Data.Vector.Hashtables -- >>> type HashTable k v = Dictionary (PrimState IO) VM.MVector k UM.MVector v ----- Different vectors could be used for keys and values:------ - storable,--- - mutable,--- - unboxed.------ In most cases unboxed vectors should be used. Nevertheless, it is up to you to decide about final form of hastable. newtype Dictionary s ks k vs v = DRef { getDRef :: MutVar s (Dictionary_ s ks k vs v) } -- | Represents collection of hashtable internal primitive arrays and vectors.@@ -70,9 +74,10 @@ hashCode, next, buckets,- refs :: IntArray s,- key :: ks s k,- value :: vs s v+ refs :: !(IntArray s),+ key :: !(ks s k),+ value :: !(vs s v),+ remSize :: {-# UNPACK #-} !FastRem } getCount, getFreeList, getFreeCount :: Int@@ -85,10 +90,11 @@ data FrozenDictionary ks k vs v = FrozenDictionary { fhashCode, fnext,- fbuckets :: A.PrimArray Int,- count, freeList, freeCount :: Int,- fkey :: ks k,- fvalue :: vs v+ fbuckets :: !(A.PrimArray Int),+ count, freeList, freeCount :: !Int,+ fkey :: !(ks k),+ fvalue :: !(vs v),+ fremSize :: {-# UNPACK #-} !FastRem } deriving (Eq, Ord, Show) -- | /O(1)/ in the best case, /O(n)/ in the worst case.@@ -96,7 +102,7 @@ -- If entry not found @-1@ returned. findElem :: (Vector ks k, Vector vs v, Hashable k, Eq k) => FrozenDictionary ks k vs v -> k -> Int-findElem FrozenDictionary{..} key' = go $ fbuckets !. (hashCode' `rem` A.sizeofPrimArray fbuckets) where+findElem FrozenDictionary{..} key' = go $ fbuckets !. (hashCode' `fastRem` fremSize) where hashCode' = hash key' .&. mask go i | i >= 0 = if fhashCode !. i == hashCode' && fkey !.~ i == key'@@ -106,15 +112,20 @@ -- | Infix version of @unsafeRead@. (!~) :: (MVector v a, PrimMonad m) => v (PrimState m) a -> Int -> m a-(!~) = V.unsafeRead+(!~) xs !i = V.unsafeRead xs i+-- Why do we need ! before i?+-- The reason is that V.unsafeRead is essentially V.basicUnsafeRead,+-- which is an opaque class member and, unless V.unsafeRead was+-- already specialised to a specific v, GHC has no clue that i is most certainly+-- to be used eagerly. Bang before i hints this vital for optimizer information. -- | Infix version of @unsafeIndex@. (!.~) :: (Vector v a) => v a -> Int -> a-(!.~) = VI.unsafeIndex+(!.~) xs !i = VI.unsafeIndex xs i -- | Infix version of @unsafeWrite@. (<~~) :: (MVector v a, PrimMonad m) => v (PrimState m) a -> Int -> a -> m ()-(<~~) = V.unsafeWrite+(<~~) xs !i x = V.unsafeWrite xs i x -- | Infix version of @readPrimArray@. (!) :: PrimMonad m => A.MutablePrimArray (PrimState m) Int -> Int -> m Int@@ -134,7 +145,8 @@ => Int -> m (Dictionary (PrimState m) ks k vs v) initialize capacity = do- let size = getPrime capacity+ let !remSize = getFastRem capacity+ size = frmPrime remSize hashCode <- A.replicate size 0 next <- A.replicate size 0 key <- V.new size@@ -169,6 +181,7 @@ -> m (FrozenDictionary ks k vs v) unsafeFreeze DRef {..} = do Dictionary {..} <- readMutVar getDRef+ let fremSize = remSize fhashCode <- A.unsafeFreeze hashCode fnext <- A.unsafeFreeze next fbuckets <- A.unsafeFreeze buckets@@ -186,6 +199,7 @@ => FrozenDictionary ks k vs v -> m (Dictionary (PrimState m) (Mutable ks) k (Mutable vs) v) unsafeThaw FrozenDictionary {..} = do+ let remSize = fremSize hashCode <- A.unsafeThaw fhashCode next <- A.unsafeThaw fnext buckets <- A.unsafeThaw fbuckets@@ -219,13 +233,7 @@ -- Find value by given key in 'Dictionary'. Throws an error if value not found. at :: (MVector ks k, MVector vs v, PrimMonad m, Hashable k, Eq k) => Dictionary (PrimState m) ks k vs v -> k -> m v-at d k = do- i <- findEntry d k- if i >= 0- then do- Dictionary{..} <- readMutVar . getDRef $ d- value !~ i- else error "KeyNotFoundException!"+at d k = fromMaybe (error "KeyNotFoundException!") <$!> at' d k {-# INLINE at #-} -- | /O(1)/ in the best case, /O(n)/ in the worst case.@@ -233,12 +241,11 @@ at' :: (MVector ks k, MVector vs v, PrimMonad m, Hashable k, Eq k) => Dictionary (PrimState m) ks k vs v -> k -> m (Maybe v) at' d k = do- i <- findEntry d k+ d_@Dictionary{..} <- readMutVar . getDRef $ d+ i <- findEntry_ d_ k if i >= 0- then do- Dictionary{..} <- readMutVar . getDRef $ d- Just <$> value !~ i- else pure Nothing+ then Just <$> value !~ i+ else return Nothing {-# INLINE at' #-} atWithOrElse :: (MVector ks k, MVector vs v, PrimMonad m, Hashable k, Eq k)@@ -259,7 +266,15 @@ findEntry :: (MVector ks k, MVector vs v, PrimMonad m, Hashable k, Eq k) => Dictionary (PrimState m) ks k vs v -> k -> m Int findEntry d key' = do- Dictionary{..} <- readMutVar . getDRef $ d+ d_ <- readMutVar . getDRef $ d+ findEntry_ d_ key'+{-# INLINE findEntry #-}++-- | /O(1)/ in the best case, /O(n)/ in the worst case.+-- Same as 'findEntry', but for 'Dictionary_'.+findEntry_ :: (MVector ks k, MVector vs v, PrimMonad m, Hashable k, Eq k)+ => Dictionary_ (PrimState m) ks k vs v -> k -> m Int+findEntry_ Dictionary{..} key' = do let hashCode' = hash key' .&. mask go i | i >= 0 = do hc <- hashCode ! i@@ -271,8 +286,8 @@ else go =<< next ! i else go =<< next ! i | otherwise = return $ -1- go =<< buckets ! (hashCode' `rem` A.length buckets)-{-# INLINE findEntry #-}+ go =<< buckets ! (hashCode' `fastRem` remSize)+{-# INLINE findEntry_ #-} -- | /O(1)/ in the best case, /O(n)/ in the worst case. -- Insert key and value in dictionary by key's hash.@@ -283,7 +298,7 @@ d@Dictionary{..} <- readMutVar getDRef let hashCode' = hash key' .&. mask- targetBucket = hashCode' `rem` A.length buckets+ !targetBucket = hashCode' `fastRem` remSize go i | i >= 0 = do hc <- hashCode ! i@@ -308,7 +323,8 @@ else do count <- refs ! getCount refs <~ getCount $ count + 1- if count == A.length next+ nextLen <- A.length next+ if count == nextLen then do nd <- resize d count hashCode' key' value' writeMutVar getDRef nd@@ -327,7 +343,20 @@ insertWithIndex :: (MVector ks k, MVector vs v, PrimMonad m, Hashable k, Eq k)- => Int -> Int -> k -> v -> MutVar (PrimState m) (Dictionary_ (PrimState m) ks k vs v) -> Dictionary_ (PrimState m) ks k vs v -> Int -> m ()+ => Int+ -- ^ Target bucket, key's hash modulo table size+ -> Int+ -- ^ Key's hash+ -> k+ -- ^ Key+ -> v+ -- ^ Value+ -> MutVar (PrimState m) (Dictionary_ (PrimState m) ks k vs v)+ -- ^ MutVar with 'Dictionary_'+ -> Dictionary_ (PrimState m) ks k vs v+ -- ^ 'Dictionary_' itself+ -> Int+ -> m () insertWithIndex !targetBucket !hashCode' key' value' getDRef d@Dictionary{..} = go where go i | i >= 0 = do@@ -343,8 +372,20 @@ {-# INLINE insertWithIndex #-} addOrResize- :: (MVector ks k, MVector vs v, PrimMonad m, Hashable k, Eq k)- => Int -> Int -> k -> v -> MutVar (PrimState m) (Dictionary_ (PrimState m) ks k vs v) -> Dictionary_ (PrimState m) ks k vs v -> m ()+ :: (MVector ks k, MVector vs v, PrimMonad m, Hashable k, Eq k)+ => Int+ -- ^ Target bucket, key's hash modulo table size+ -> Int+ -- ^ Key's hash+ -> k+ -- ^ Key+ -> v+ -- ^ Value+ -> MutVar (PrimState m) (Dictionary_ (PrimState m) ks k vs v)+ -- ^ MutVar with 'Dictionary_'+ -> Dictionary_ (PrimState m) ks k vs v+ -- ^ 'Dictionary_' itself+ -> m () addOrResize !targetBucket !hashCode' !key' !value' dref d@Dictionary{..} = do freeCount <- refs ! getFreeCount if freeCount > 0@@ -357,7 +398,8 @@ else do count <- refs ! getCount refs <~ getCount $ count + 1- if count == A.length next+ nextLen <- A.length next+ if count == nextLen then do nd <- resize d count hashCode' key' value' writeMutVar dref nd@@ -375,9 +417,22 @@ buckets <~ targetBucket $ index {-# INLINE add #-} -+resize+ :: (MVector ks k, MVector vs v, PrimMonad m, Hashable k, Eq k)+ => Dictionary_ (PrimState m) ks k vs v+ -- ^ The original 'Dictionary_'+ -> Int+ --+ -> Int+ -- ^ Key's hash+ -> k+ -- ^ Key+ -> v+ -- ^ Value+ -> m (Dictionary_ (PrimState m) ks k vs v) resize Dictionary{..} index hashCode' key' value' = do- let newSize = getPrime (index*2)+ let !newRemSize = getFastRem (index*2)+ newSize = frmPrime newRemSize delta = newSize - index buckets <- A.replicate newSize (-1)@@ -390,7 +445,7 @@ let go i | i < index = do hc <- hashCode ! i when (hc >= 0) $ do- let bucket = hc `rem` newSize+ let !bucket = hc `fastRem` newRemSize nx <- buckets ! bucket next <~ i $ nx buckets <~ bucket $ i@@ -398,13 +453,14 @@ | otherwise = return () go 0 - let targetBucket = hashCode' `rem` A.length buckets+ let !targetBucket = hashCode' `fastRem` newRemSize hashCode <~ index $ hashCode' b <- buckets ! targetBucket next <~ index $ b key <~~ index $ key' value <~~ index $ value' buckets <~ targetBucket $ index+ let remSize = newRemSize return Dictionary{..} {-# INLINE resize #-}@@ -428,7 +484,7 @@ delete DRef{..} key' = do Dictionary{..} <- readMutVar getDRef let hashCode' = hash key' .&. mask- bucket = hashCode' `rem` A.length buckets+ !bucket = hashCode' `fastRem` remSize go !last !i | i >= 0 = do hc <- hashCode ! i k <- key !~ i@@ -542,9 +598,61 @@ {-# INLINE findWithDefault #-} -- | /O(1)/ in the best case, /O(n)/ in the worst case.+-- The expression (@'upsert' ht f k@) updates or inserts the value @x@ at @k@.+--+-- It's a responsibility of 'MVector' @vs@ to force evaluation of the updated value.+-- Unboxed / storable vectors do it automatically. If you use boxed vectors,+-- consider employing strict ones from+-- [@strict-containers@](https://hackage.haskell.org/package/strict-containers)+-- to eliminate potential accumulation of thunks.+--+-- > let f _ = "c"+-- > ht <- fromList [(5,"a"), (3,"b")]+-- > upsert ht f 7+-- > toList ht+-- > [(3, "b"), (5, "a"), (7, "c")]+--+-- > ht <- fromList [(5,"a"), (3,"b")]+-- > upsert ht f 5+-- > toList ht+-- > [(3, "b"), (5, "c")]+--+upsert+ :: ( MVector ks k, MVector vs v+ , PrimMonad m, Hashable k, Eq k+ )+ => Dictionary (PrimState m) ks k vs v -> (Maybe v -> v) -> k -> m ()+upsert ht f k = do+ d@Dictionary{..} <- readMutVar . getDRef $ ht+ let+ hashCode' = hash k .&. mask+ !targetBucket = hashCode' `fastRem` remSize++ onFound' value' dict i = insertWithIndex targetBucket hashCode' k value' (getDRef ht) dict i++ onFound dict i = do+ d'@Dictionary{..} <- readMutVar . getDRef $ dict+ v <- value !~ i+ onFound' (f (Just v)) d' i++ onNothing dict = do+ d' <- readMutVar . getDRef $ dict+ onFound' (f Nothing) d' (-1)++ void $ atWithOrElse ht k onFound onNothing++{-# INLINE upsert #-}++-- | /O(1)/ in the best case, /O(n)/ in the worst case. -- The expression (@'alter' ht f k@) alters the value @x@ at @k@, or absence thereof. -- 'alter' can be used to insert, delete, or update a value in a 'Dictionary'. --+-- It's a responsibility of 'MVector' @vs@ to force evaluation of the updated value.+-- Unboxed / storable vectors do it automatically. If you use boxed vectors,+-- consider employing strict ones from+-- [@strict-containers@](https://hackage.haskell.org/package/strict-containers)+-- to eliminate potential accumulation of thunks.+-- -- > let f _ = Nothing -- > ht <- fromList [(5,"a"), (3,"b")] -- > alter ht f 7@@ -576,7 +684,7 @@ d@Dictionary{..} <- readMutVar . getDRef $ ht let hashCode' = hash k .&. mask- targetBucket = hashCode' `rem` A.length buckets+ !targetBucket = hashCode' `fastRem` remSize onFound' value' dict i = insertWithIndex targetBucket hashCode' k value' (getDRef ht) dict i onNothing' dict i = deleteWithIndex targetBucket hashCode' d k (-1) i@@ -610,7 +718,7 @@ d@Dictionary{..} <- readMutVar . getDRef $ ht let hashCode' = hash k .&. mask- targetBucket = hashCode' `rem` A.length buckets+ !targetBucket = hashCode' `fastRem` remSize onFound' value' dict i = insertWithIndex targetBucket hashCode' k value' (getDRef ht) dict i onNothing' dict i = deleteWithIndex targetBucket hashCode' d k (-1) i@@ -692,7 +800,7 @@ v <- value dictS !~ i let hashCode' = hash k .&. mask- targetBucket = hashCode' `rem` A.length (buckets dictG)+ !targetBucket = hashCode' `fastRem` remSize dictG onFound dict i = do d@Dictionary{..} <- readMutVar . getDRef $ dict@@ -856,17 +964,42 @@ -- * Extras -primes :: UI.Vector Int-primes = UI.fromList [- 3, 7, 11, 17, 23, 29, 37, 47, 59, 71, 89, 107, 131, 163, 197, 239, 293, 353, 431, 521, 631,- 761, 919, 1103, 1327, 1597, 1931, 2333, 2801, 3371, 4049, 4861, 5839, 7013, 8419, 10103, 12143,- 14591, 17519, 21023, 25229, 30293, 36353, 43627, 52361, 62851, 75431, 90523, 108631, 130363,- 156437, 187751, 225307, 270371, 324449, 389357, 467237, 560689, 672827, 807403, 968897,- 1162687, 1395263, 1674319, 2009191, 2411033, 2893249, 3471899, 4166287, 4999559, 5999471,- 7199369, 8639249, 10367101, 12440537, 14928671, 17914409, 21497293, 25796759, 30956117,- 37147349, 44576837, 53492207, 64190669, 77028803, 92434613, 110921543, 133105859, 159727031,- 191672443, 230006941, 276008387, 331210079, 397452101, 476942527, 572331049, 686797261,- 824156741, 988988137, 1186785773, 1424142949, 1708971541, 2050765853 ]+-- | This data is auto-generated by GenPrimes.hs.+-- The vector contains tuples (p, m, s) such that p is prime+-- and (assuming 64-bit architecture) for every n >= 0+-- it holds that n \`'quot'\` p = (n * m) \`'shiftR'\` (64 + s),+-- enabling faster computation of remainders.+primesWithFastRem :: UI.Vector (Int, Int, Int)+primesWithFastRem = UI.fromList $+ if finiteBitSize (0 :: Int) == 32+ then [(5,1717986919,1),(11,780903145,1),(17,2021161081,3),(41,1676084799,4),(53,1296593901,4),(67,128207979,1),(83,827945503,4),(101,680390859,4),(131,1049152317,5),(163,210795941,3),(197,1395319325,6),(241,285143057,4),(311,883851791,6),(379,1450543045,7),(457,601483385,6),(557,123374285,4),(673,1633746847,8),(809,84943729,4),(977,562697865,7),(1187,1852589095,9),(1427,24078303,3),(1721,638879505,8),(2069,2125687053,10),(2503,1757110073,10),(3041,723125043,9),(3673,299349749,8),(4409,1995031305,11),(5297,103786259,7),(6367,1381512961,11),(7649,287491601,9),(9181,1916151405,12),(11047,1592485385,12),(13291,330904109,10),(15959,1102336365,12),(19157,57394771,8),(22993,382555257,11),(27611,637144111,12),(33149,1061400709,13),(39779,1768992287,14),(47741,736984397,13),(57301,1228054383,14),(68767,511646169,13),(82529,426327377,13),(99041,177625287,12),(118861,1184051021,15),(142657,1973089135,16),(171203,1644100727,16),(205477,684930617,15),(246577,285382433,14),(295901,951247129,16),(355087,792692993,16),(426131,1321072519,17),(511361,1100885585,17),(613637,917398973,17),(736369,1528988737,18),(883661,637065519,17),(1060421,2123496059,19),(1272539,1769533047,19),(1527061,184324645,16),(1832497,1228815007,19),(2199061,2047964849,20),(2638879,1706633623,20),(3166661,1422191901,20),(3800051,1185141891,20),(4560071,123452015,17),(5472109,411504927,19),(6566551,685839435,20),(7879897,285765133,19),(9455881,238137495,19),(11347079,793790125,21),(13616503,1322982745,22),(16339877,275620167,20),(19607893,229682997,20),(23529511,191402177,20),(28235483,1276011359,23),(33882593,2126684757,24),(40659149,1772235667,24),(48791009,738431071,23),(58549219,1230718279,24),(70259107,1025597921,24),(84310943,427332393,23),(101173139,712220603,24),(121407857,74189591,21),(145689433,1978389031,26),(174827333,206082175,23),(209792827,1373880987,26),(251751509,286225073,24),(302101841,1908166963,27),(362522213,1590139119,27),(435026701,662557897,26),(522032051,1104263141,27),(626438489,1840438487,28),(751726211,766849345,27),(902071483,1278082199,28)]+ else [(5,7378697629483820647,1),(7,5270498306774157605,1),(11,3353953467947191203,1),(17,8680820740569200761,3),(29,5088756985850910791,3),(37,3988485205126389539,3),(47,6279742663390485657,4),(67,8810385229234412713,5),(83,3555998857582564167,4),(107,1379195818595106663,3),(131,281629680514649643,1),(163,7242893378634425175,6),(197,1498212716646461045,4),(241,1224680104478642431,4),(293,503665367200260795,3),(353,1672226091667721393,5),(433,681634884940768651,4),(521,9064043153300662599,8),(631,7483940543375032035,8),(761,3102737505170594753,7),(919,642324059149842929,5),(1103,4281383937325154319,8),(1327,7117357170866081709,9),(1597,739255867700320165,6),(1949,2422968949650921095,8),(2339,4037936282915472607,9),(2833,6667654758728761333,10),(3407,5544310517017487777,10),(4093,4615066193862345677,10),(4919,7680205705012637063,11),(5903,6399954576140464461,11),(7103,2659364484228999135,10),(8527,8861013688977873041,12),(10243,7376536534795892163,12),(12301,6142416366629893783,12),(14767,2558334926725615339,11),(17729,4261823212020662385,12),(21277,887788030806907969,10),(25561,1477991153043979567,11),(30677,4926026907840683471,13),(36821,4104063644437376683,13),(44201,1709415255897249461,12),(53051,5696998264003643545,14),(63667,4747066060968119963,14),(76403,7911507529904775825,15),(91691,6592390854143968191,15),(110039,5493169783506889261,15),(132047,9155269105808001505,16),(158507,3813477700084630883,15),(190243,6354640221267690137,16),(228299,5295361870243098633,16),(273967,8825338961368552963,17),(328777,3677038903617434233,16),(394549,6128140330426026551,17),(473471,5106652021410515849,17),(568171,8510999819523553119,18),(681809,3546230160102401625,17),(818173,5910367707634591583,18),(981809,4925299399841024781,18),(1178173,8208817004732779819,19),(1413827,1710146743009758867,17),(1696601,5700460247823167261,19),(2035927,4750370006840634953,19),(2443151,7917158257444614269,20),(2931793,6597605326786054403,20),(3518209,5497914738389352877,20),(4221851,9163190796564855935,21),(5066231,7635977559583866901,21),(6079481,3181655327787695495,20),(7295381,1325689029389559421,19),(8754461,8837923026367501915,22),(10505377,7364919169996114103,22),(12606463,1534356870268374785,20),(15127831,5114497409135273073,22),(18153427,8524148355606494265,23),(21784129,7103451550010217731,23),(26140973,5919538837007808943,23),(31369243,2466468586931991543,22),(37643093,8221561650668425911,24),(45171733,856412266221181587,21),(54206099,5709413064779759723,24),(65047343,4757842450557051481,24),(78056833,247804226362015825,20),(93668203,6608112463123586747,25),(112401881,1376689638411589699,23),(134882263,9177930528088635901,26),(161858731,7648274712381010049,26),(194230481,3186781067811339753,25),(233076601,663912654667005953,23),(279691949,4426083924515754563,26),(335630353,7376806228758340429,27),(402756463,48026077520285669,20),(483307787,5122781269342057073,27),(579969349,4268984357259129137,27),(695963227,7114973844934943811,28),(835155913,5929144582541584783,28),(1002187163,617619185811393333,25),(1202624651,8234922098136039455,29),(1443149623,6862434883013541971,29),(1731779563,357418480311913773,25),(2078135531,4765579610448921293,29),(2493762643,7942632665608538591,30),(2992515199,3309430247035160405,29),(3591018241,5515717075012787271,30),(4309221899,9192861770781641709,31),(5171066297,7660718115355497017,31),(6205279567,6383931751891069761,31),(7446335483,1329985781179588333,29),(8935602619,2216642958858742015,30),(10722723161,3694404925160612267,31),(12867267797,6157341540115949263,32),(15440721377,1282779485812755651,30),(18528865703,8551863215397642261,33),(22234638851,890819084640777303,30),(26681566631,2969396947711947027,32),(32017880003,309312181610871977,29),(38421456013,4124162420469296211,33),(46105747229,6873604032117775405,34),(55326896741,1432000838311112129,32),(66392276177,1193334030347810553,32),(79670731433,3977780100130856927,34),(95604877727,6629633499704948593,35),(114725853301,172646705678443951,30),(137671023989,575489018811937629,32),(165205228889,3836593456372840199,35),(198246274687,6394322426636527325,36),(237895529659,5328602021422103605,36),(285474635629,4440501683924226201,36),(342569562761,7400836139739766199,37),(411083475323,3083681724818056003,36),(493300170481,1284867385097583665,35),(591960204599,8565782567001774073,38),(710352245527,7138152139085745311,38),(852422694637,5948460115872687585,38),(1022907233639,4957050096199058351,38),(1227488680427,4130875079963290535,38),(1472986416527,3442395899935288315,38),(1767583699907,5737326499650006151,39),(2121100439917,2390552708155269385,38),(2545320527903,996063628397011449,37),(3054384633659,6640424188932079035,40),(3665261560423,5533686824061451401,40),(4398313872521,9222811373407653915,41),(5277976647059,7685676144457159429,41),(6333571976483,6404730120368629123,41),(7600286371789,2668637550150294909,40),(9120343646191,8895458500457872893,42),(10944412375433,7412882083712320257,42),(13133294850551,6177401736412164183,42),(15759953820697,5147834780331776433,42),(18911944584839,1072465579235639315,40),(22694333501813,7149770528235642145,43),(27233200202177,2979071053431364413,42),(32679840242663,1241279605594479897,41),(39215808291301,8275197370607624801,44),(47058969949679,3447998904411212491,43),(56470763939783,1436666210167059381,42),(67764916727749,1197221841805716745,42),(81317900073323,7981478945369069699,45),(97581480088031,3325616227235633285,44),(117097776105689,5542693712056936913,45),(140517331326899,4618911426711740825,45),(168620797592327,3849092855592017097,45),(202344957110837,3207577379659307247,45),(242813948533111,5345962299429831765,46),(291376738239791,8909937165714618823,47),(349652085887761,3712473819047632555,46),(419582503065331,1546864091269781275,45),(503499003678427,5156213637565632409,47),(604198804414123,8593689395942569915,48),(725038565296949,7161407829952127767,48),(870046278356531,745979982286515183,45),(1044055534027841,4973199881910083119,48),(1252866640833481,259020827182801985,44),(1503439969000181,6907222058208035475,49),(1804127962800257,2878009190919951291,48),(2164953555360361,299792624054154309,45),(2597944266432433,999308746847181107,47),(3117533119718951,1665514578078618403,48),(3741039743662841,693964407532739155,47),(4489247692395509,4626429383551491517,50),(5387097230874631,7710715639252456949,51),(6464516677049609,1606399091510915659,49),(7757420012459563,5354663638369696637,51),(9308904014951479,2231109849320706117,50),(11170684817941799,7437032831069004279,52),(13404821781530213,3098763679612072587,51),(16085786137836413,5164606132686737109,52),(19302943365403697,8607676887811227891,53),(23163532038484451,7173064073176018721,53),(27796238446181363,2988776697156672123,52),(33355486135417657,4981294495261117009,53),(40026583362501191,8302157492101861143,54),(48031900035001457,1729616144187886737,52),(57638280042001759,2882693573646477365,53),(69165936050402159,1201122322352698065,52),(82999123260482599,8007482149017986309,55),(99598947912579133,6672901790848320973,55),(119518737495095009,347546968273349907,51),(143422484994114059,4633959576977997203,55),(172106981992936889,7723265961629994521,56),(206528378391524347,6436054968024992935,56),(247834054069829309,2681689570010412721,55),(297400864883795143,8938965233368043239,57),(356881037860554209,7449137694473368585,57),(428257245432665101,6207614745394473093,57),(513908694519198103,5173012287828727761,57),(616690433423037709,8621687146381213139,58),(740028520107645211,898092411081376417,55),(888034224129174191,2993641370271254933,57),(1065641068955008969,2494701141892712585,57),(1278769282746010901,4157835236487853859,58),(1534523139295213087,216553918567075721,54),(1841427767154255641,1443692790447171523,57),(2209713320585106689,2406154650745285959,58),(2651655984702128147,8020515502484286167,60),(3181987181642553871,1670940729684226235,58),(3818384617971064327,5569802432280754581,60),(4582061541565277261,1160375506725157187,58)] -getPrime :: Int -> Int-getPrime n = fromJust $ UI.find (>= n) primes+getFastRem :: Int -> FastRem+getFastRem n =+ (\(p, m, s) -> FastRem p m s) $ fromJust $+ UI.find (\(p, _, _) -> p >= n) primesWithFastRem++-- | For 64-bit architectures+-- 'frmPrime' is a prime number such that for each @n@ >= 0+-- it holds that @n@ \`'quot'\` 'frmPrime' = (n * '_frmMulHi') \`'shiftR'\` (64 + s).+data FastRem = FastRem+ { frmPrime :: !Int+ , _frmMulHi :: !Int+ , _frmShift :: !Int+ } deriving (Eq, Ord, Show)++fastRem :: Int -> FastRem -> Int+#ifndef aarch64_HOST_ARCH+fastRem !i (FastRem !p !m !s) = i - p * q+ where+ q = fromIntegral (mulHi (fromIntegral i) (fromIntegral m)) `unsafeShiftR` s+ mulHi (Exts.W# x) (Exts.W# y) =+ let (# z, _ #) = Exts.timesWord2# x y in Exts.W# z+#else+-- At the moment GHC NCG does not make use of UMULH instruction,+-- so timesWord2# is painfully slow on ARM. This is being worked on+-- at https://gitlab.haskell.org/ghc/ghc/-/merge_requests/10832,+-- but in the meantime we should resort to a usual division.+fastRem !i (FastRem !p _ _) = i `rem` p+#endif
+ src/Data/Vector/Hashtables/Internal/Mask.hs view
@@ -0,0 +1,11 @@+{-|+Module : Data.Vector.Hashtables.Internal.Mask+Description : Provides arch-dependent mask for hashtables.+Copyright : (c) klapaucius, swamp_agr, 2016-2021+License : BSD3+-}+module Data.Vector.Hashtables.Internal.Mask where++-- | 'Int' mask. For 32-bit it is equal to @0x7FFFFFFF@. Otherwise, @0x7FFFFFFFFFFFFFFF@.+mask = maxBound :: Int+{-# INLINE mask #-}
test/Data/Vector/HashTablesSpec.hs view
@@ -117,6 +117,8 @@ testAlter :: VH.Dictionary (PrimState IO) ks Int vs Int -> (Maybe Int -> Maybe Int) -> Int -> IO () + testUpsert :: VH.Dictionary (PrimState IO) ks Int vs Int -> (Maybe Int -> Int) -> Int -> IO ()+ testUnion :: VH.Dictionary (PrimState IO) ks Int vs Int -> VH.Dictionary (PrimState IO) ks Int vs Int@@ -177,6 +179,11 @@ it "when altering is nothing - key deleted from table" $ property prop_alterDelete it "when altering is just a result - key updated with result" $ property prop_alterUpdate++ it "when upserting a new key - key is set to value" $ property prop_upsertInsert++ it "when upserting an existing key - key updated with result" $ property prop_upsertUpdate+ it "intersection + symmetric difference of two tables is equal to union of two tables" $ property prop_union where@@ -302,6 +309,21 @@ v <- testAt ht x v `shouldBe` (negate y) + prop_upsertInsert :: HashTableTest ks vs => (Int, Int) -> IO ()+ prop_upsertInsert (x, y) = do+ ht <- testInit (Proxy @ks) (Proxy @vs) 10+ testUpsert ht (maybe 0 negate) x+ v <- testAt ht x+ v `shouldBe` 0++ prop_upsertUpdate :: HashTableTest ks vs => (Int, Int) -> IO ()+ prop_upsertUpdate (x, y) = do+ ht <- testInit (Proxy @ks) (Proxy @vs) 10+ testInsert ht x y+ testUpsert ht (maybe 0 negate) x+ v <- testAt ht x+ v `shouldBe` (negate y)+ prop_union :: Positive Int -> Property prop_union (Positive n) = forAll (twoListsN n) $ \(xs, ys) -> do ht1 <- testFromList (Proxy @ks) (Proxy @vs) xs@@ -337,6 +359,7 @@ testNull = VH.null testMember = VH.member testAlter = VH.alter+ testUpsert = VH.upsert testUnion = VH.union testDifference = VH.difference testIntersection = VH.intersection@@ -361,6 +384,7 @@ testNull = VH.null testMember = VH.member testAlter = VH.alter+ testUpsert = VH.upsert testUnion = VH.union testDifference = VH.difference testIntersection = VH.intersection@@ -385,6 +409,7 @@ testNull = VH.null testMember = VH.member testAlter = VH.alter+ testUpsert = VH.upsert testUnion = VH.union testDifference = VH.difference testIntersection = VH.intersection@@ -409,6 +434,7 @@ testNull = VH.null testMember = VH.member testAlter = VH.alter+ testUpsert = VH.upsert testUnion = VH.union testDifference = VH.difference testIntersection = VH.intersection
vector-hashtables.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.0 name: vector-hashtables-version: 0.1.1.4+version: 0.1.2.0 synopsis: Efficient vector-based mutable hashtables implementation. description: This package provides efficient vector-based hashtable implementation similar to .NET Generic Dictionary implementation (at the time of 2015).@@ -11,26 +11,24 @@ license-file: LICENSE author: klapaucius maintainer: klapaucius, swamp_agr, ArtemPelenitsyn-copyright: 2016-2023 klapaucius, swamp_agr+copyright: 2016-2024 klapaucius, swamp_agr category: Data build-type: Simple extra-doc-files: README.md, changelog.md-extra-source-files: src-i386/Data/Vector/Hashtables/Internal/Mask.hs,- src-gen/Data/Vector/Hashtables/Internal/Mask.hs+extra-source-files: gen/GenPrimes.hs tested-with:- GHC == 9.4.4- GHC == 9.2.5+ GHC == 9.8.1+ GHC == 9.6.3+ GHC == 9.4.7+ GHC == 9.2.8 GHC == 9.0.2 GHC == 8.10.7 GHC == 8.8.4 GHC == 8.6.5 library- if arch(i386) || arch(wasm32)- hs-source-dirs: src, src-i386- else- hs-source-dirs: src, src-gen+ hs-source-dirs: src exposed-modules: Data.Vector.Hashtables, Data.Vector.Hashtables.Internal, Data.Vector.Hashtables.Internal.Mask,@@ -73,7 +71,7 @@ -- Additional dependencies build-depends: hspec >= 2.6.0 && < 2.12- , QuickCheck >= 2.12.6.1 && < 2.15+ , QuickCheck >= 2.12.6.1 && < 2.16 , quickcheck-instances >= 0.3.19 && < 0.4 build-tool-depends: