Skip to content

Commit 9bd992e

Browse files
committed
Alternative approach to page allocation
This is a direct style, not using arenas. It's more like a slab allocator, but only for 4k page allocations. This allows the slab cache to be manipulated only using atomic IORef operations.
1 parent fc0fb88 commit 9bd992e

File tree

12 files changed

+382
-317
lines changed

12 files changed

+382
-317
lines changed

bench/macro/lsm-tree-bench-lookups.hs

+23-21
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,10 @@
33
module Main ( main ) where
44

55
import Control.DeepSeq
6-
import Control.Exception (assert)
6+
import Control.Exception (assert, evaluate)
77
import Control.Monad
8-
import Control.Monad.Class.MonadST
98
import Control.Monad.Primitive
109
import Control.Monad.ST.Strict (ST, runST)
11-
import Data.Arena (ArenaManager, newArenaManager, withArena)
1210
import Data.Bits ((.&.))
1311
import Data.BloomFilter (Bloom)
1412
import qualified Data.BloomFilter as Bloom
@@ -26,6 +24,7 @@ import Database.LSMTree.Internal.Entry (Entry (Insert),
2624
NumEntries (..))
2725
import Database.LSMTree.Internal.IndexCompact (IndexCompact)
2826
import Database.LSMTree.Internal.Lookup
27+
import Database.LSMTree.Internal.PageAlloc
2928
import Database.LSMTree.Internal.Paths (RunFsPaths (RunFsPaths))
3029
import Database.LSMTree.Internal.Run (Run)
3130
import qualified Database.LSMTree.Internal.Run as Run
@@ -136,7 +135,7 @@ benchmarks !caching = withFS $ \hfs hbio -> do
136135
#ifdef NO_IGNORE_ASSERTS
137136
putStrLn "BENCHMARKING A BUILD WITH -fno-ignore-asserts"
138137
#endif
139-
arenaManager <- newArenaManager
138+
pagealloc <- newPageAlloc
140139
enabled <- getRTSStatsEnabled
141140
when (not enabled) $ fail "Need RTS +T statistics enabled"
142141
let runSizes = lsmStyleRuns benchmarkSizeBase
@@ -197,17 +196,17 @@ benchmarks !caching = withFS $ \hfs hbio -> do
197196
_bindexSearches <-
198197
benchmark "benchIndexSearches"
199198
"Calculate batches of keys, perform bloom queries for each batch, and perform index searches for positively queried keys in each batch. Net time/allocation is the result of subtracting the cost of benchGenKeyBatches and benchBloomQueries."
200-
(benchIndexSearches arenaManager blooms indexes handles keyRng0) benchmarkNumLookups
199+
(benchIndexSearches pagealloc blooms indexes handles keyRng0) benchmarkNumLookups
201200
(x1 + x2, y1 + y2)
202201
_bprepLookups <-
203202
benchmark "benchPrepLookups"
204203
"Calculate batches of keys, and prepare lookups for each batch. This is roughly doing the same amount of work as benchIndexSearches. Net time/allocation is the result of subtracting the cost of benchGenKeyBatches."
205-
(benchPrepLookups arenaManager blooms indexes handles keyRng0) benchmarkNumLookups
204+
(benchPrepLookups pagealloc blooms indexes handles keyRng0) benchmarkNumLookups
206205
bgenKeyBatches
207206
_blookupsIO <-
208207
benchmark "benchLookupsIO"
209208
"Calculate batches of keys, and perform disk lookups for each batch. This is roughly doing the same as benchPrepLookups, but also performing the disk I/O and resolving values. Net time/allocation is the result of subtracting the cost of benchGenKeyBatches."
210-
(benchLookupsIO hbio arenaManager benchmarkResolveSerialisedValue runs blooms indexes handles keyRng0) benchmarkNumLookups
209+
(benchLookupsIO hbio pagealloc benchmarkResolveSerialisedValue runs blooms indexes handles keyRng0) benchmarkNumLookups
211210
bgenKeyBatches
212211

213212
traceMarkerIO "Cleaning up"
@@ -303,8 +302,8 @@ withFS ::
303302
(FS.HasFS IO FS.HandleIO -> FS.HasBlockIO IO FS.HandleIO -> IO a)
304303
-> IO a
305304
withFS action = do
306-
let hfs = FS.ioHasFS (FS.MountPoint "")
307-
exists <- FS.doesDirectoryExist hfs (FS.mkFsPath ["_bench_lookups"])
305+
let hfs = FS.ioHasFS (FS.MountPoint "_bench_lookups")
306+
exists <- FS.doesDirectoryExist hfs (FS.mkFsPath [""])
308307
unless exists $ error ("_bench_lookups directory does not exist")
309308
FS.withIOHasBlockIO hfs FS.defaultIOCtxParams $ \hbio ->
310309
action hfs hbio
@@ -427,43 +426,46 @@ benchBloomQueries !bs !keyRng !n
427426
-- | This gives us the combined cost of calculating batches of keys, performing
428427
-- bloom queries for each batch, and performing index searches for each batch.
429428
benchIndexSearches
430-
:: ArenaManager RealWorld
429+
:: PageAlloc RealWorld
431430
-> V.Vector (Bloom SerialisedKey)
432431
-> V.Vector IndexCompact
433432
-> V.Vector (FS.Handle h)
434433
-> StdGen
435434
-> Int
436435
-> IO ()
437-
benchIndexSearches !arenaManager !bs !ics !hs !keyRng !n
436+
benchIndexSearches !pagealloc !bs !ics !hs !keyRng !n
438437
| n <= 0 = pure ()
439438
| otherwise = do
440439
let (!ks, !keyRng') = genLookupBatch keyRng benchmarkGenBatchSize
441440
!rkixs = bloomQueriesDefault bs ks
442-
!_ioops <- withArena arenaManager $ \arena -> stToIO $ indexSearches arena ics hs ks rkixs
443-
benchIndexSearches arenaManager bs ics hs keyRng' (n-benchmarkGenBatchSize)
441+
withPages pagealloc (VU.length rkixs) $ \pages -> do
442+
_ <- evaluate (indexSearches ics hs ks pages rkixs)
443+
return ()
444+
benchIndexSearches pagealloc bs ics hs keyRng' (n-benchmarkGenBatchSize)
444445

445446
-- | This gives us the combined cost of calculating batches of keys, and
446447
-- preparing lookups for each batch.
447448
benchPrepLookups
448-
:: ArenaManager RealWorld
449+
:: PageAlloc RealWorld
449450
-> V.Vector (Bloom SerialisedKey)
450451
-> V.Vector IndexCompact
451452
-> V.Vector (FS.Handle h)
452453
-> StdGen
453454
-> Int
454455
-> IO ()
455-
benchPrepLookups !arenaManager !bs !ics !hs !keyRng !n
456+
benchPrepLookups !pagealloc !bs !ics !hs !keyRng !n
456457
| n <= 0 = pure ()
457458
| otherwise = do
458459
let (!ks, !keyRng') = genLookupBatch keyRng benchmarkGenBatchSize
459-
(!_rkixs, !_ioops) <- withArena arenaManager $ \arena -> stToIO $ prepLookups arena bs ics hs ks
460-
benchPrepLookups arenaManager bs ics hs keyRng' (n-benchmarkGenBatchSize)
460+
withPreparedLookups pagealloc bs ics hs ks $ \rkixs ioops ->
461+
void $ evaluate rkixs >> evaluate ioops
462+
benchPrepLookups pagealloc bs ics hs keyRng' (n-benchmarkGenBatchSize)
461463

462464
-- | This gives us the combined cost of calculating batches of keys, and
463465
-- performing disk lookups for each batch.
464466
benchLookupsIO ::
465467
FS.HasBlockIO IO h
466-
-> ArenaManager RealWorld
468+
-> PageAlloc RealWorld
467469
-> ResolveSerialisedValue
468470
-> V.Vector (Run (FS.Handle h))
469471
-> V.Vector (Bloom SerialisedKey)
@@ -472,12 +474,12 @@ benchLookupsIO ::
472474
-> StdGen
473475
-> Int
474476
-> IO ()
475-
benchLookupsIO !hbio !arenaManager !resolve !rs !bs !ics !hs !keyRng !n
477+
benchLookupsIO !hbio !pagealloc !resolve !rs !bs !ics !hs !keyRng !n
476478
| n <= 0 = pure ()
477479
| otherwise = do
478480
let (!ks, !keyRng') = genLookupBatch keyRng benchmarkGenBatchSize
479-
!_ <- lookupsIO hbio arenaManager resolve rs bs ics hs ks
480-
benchLookupsIO hbio arenaManager resolve rs bs ics hs keyRng' (n-benchmarkGenBatchSize)
481+
!_ <- lookupsIO hbio pagealloc resolve rs bs ics hs ks
482+
benchLookupsIO hbio pagealloc resolve rs bs ics hs keyRng' (n-benchmarkGenBatchSize)
481483

482484
{-------------------------------------------------------------------------------
483485
Utilities

bench/micro/Bench/Database/LSMTree/Internal/Lookup.hs

+38-28
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,10 @@
22

33
module Bench.Database.LSMTree.Internal.Lookup (benchmarks) where
44

5-
import Control.Exception (assert)
5+
import Control.Exception (assert, evaluate)
66
import Control.Monad
7-
import Control.Monad.ST.Strict (stToIO)
87
import Criterion.Main (Benchmark, bench, bgroup, env, envWithCleanup,
9-
perRunEnv, perRunEnvWithCleanup, whnf, whnfAppIO)
10-
import Data.Arena (ArenaManager, closeArena, newArena,
11-
newArenaManager, withArena)
8+
perRunEnvWithCleanup, whnf, whnfAppIO)
129
import Data.Bifunctor (Bifunctor (..))
1310
import qualified Data.List as List
1411
import Data.Map.Strict (Map)
@@ -21,7 +18,10 @@ import Database.LSMTree.Extras.Random (frequency,
2118
import Database.LSMTree.Extras.UTxO
2219
import Database.LSMTree.Internal.Entry (Entry (..), NumEntries (..))
2320
import Database.LSMTree.Internal.Lookup (bloomQueriesDefault,
24-
indexSearches, intraPageLookups, lookupsIO, prepLookups)
21+
cleanupPreparedLookups, indexSearches, intraPageLookups,
22+
lookupsIO, unmanagedAllocatePagesForIndexSearches,
23+
unmanagedPreparedLookups, withPreparedLookups)
24+
import Database.LSMTree.Internal.PageAlloc
2525
import Database.LSMTree.Internal.Paths (RunFsPaths (..))
2626
import Database.LSMTree.Internal.Run (Run)
2727
import qualified Database.LSMTree.Internal.Run as Run
@@ -77,7 +77,7 @@ benchmarks = bgroup "Bench.Database.LSMTree.Internal.Lookup" [
7777

7878
benchLookups :: Config -> Benchmark
7979
benchLookups conf@Config{name} =
80-
withEnv $ \ ~(_dir, arenaManager, _hasFS, hasBlockIO, rs, ks) ->
80+
withEnv $ \ ~(_dir, pagealloc, _hasFS, hasBlockIO, rs, ks) ->
8181
env ( pure ( V.map Run.runFilter rs
8282
, V.map Run.runIndex rs
8383
, V.map Run.runKOpsFile rs
@@ -90,24 +90,33 @@ benchLookups conf@Config{name} =
9090
whnf (\ks' -> bloomQueriesDefault blooms ks') ks
9191
-- The compact index is only searched for (true and false) positive
9292
-- lookup keys. We use whnf here because the result is
93-
, env (pure $ bloomQueriesDefault blooms ks) $ \rkixs ->
94-
bench "Compact index search" $
95-
whnfAppIO (\ks' -> withArena arenaManager $ \arena -> stToIO $ indexSearches arena indexes kopsFiles ks' rkixs) ks
93+
, env (do let !rkixs = bloomQueriesDefault blooms ks
94+
pages <- unmanagedAllocatePagesForIndexSearches pagealloc rkixs
95+
return (rkixs, pages))
96+
(\ ~(rkixs, pages) ->
97+
bench "Compact index search" $
98+
whnf (\ks' -> indexSearches indexes kopsFiles
99+
ks' pages rkixs) ks)
96100
-- prepLookups combines bloom filter querying and index searching.
97101
-- The implementation forces the results to WHNF, so we use
98102
-- whnfAppIO here instead of nfAppIO.
99103
, bench "Lookup preparation in memory" $
100-
whnfAppIO (\ks' -> withArena arenaManager $ \arena -> stToIO $ prepLookups arena blooms indexes kopsFiles ks') ks
104+
whnfAppIO (\ks' -> withPreparedLookups
105+
pagealloc blooms indexes
106+
kopsFiles ks' $ \rkixs ioops ->
107+
void $ evaluate rkixs >> evaluate ioops
108+
) ks
101109
-- Submit the IOOps we get from prepLookups to HasBlockIO. We use
102110
-- perRunEnv because IOOps contain mutable buffers, so we want fresh
103111
-- ones for each run of the benchmark. We manually evaluate the
104112
-- result to WHNF since it is unboxed vector.
105113
, bench "Submit IOOps" $
106114
-- TODO: here arena is destroyed too soon
107115
-- but it should be fine for non-debug code
108-
perRunEnv (withArena arenaManager $ \arena -> stToIO $ prepLookups arena blooms indexes kopsFiles ks) $ \ ~(_rkixs, ioops) -> do
109-
!_ioress <- FS.submitIO hasBlockIO ioops
110-
pure ()
116+
perRunEnvWithCleanup
117+
(unmanagedPreparedLookups pagealloc blooms indexes kopsFiles ks)
118+
(cleanupPreparedLookups pagealloc)
119+
(\ ~(_, ioops, _) -> void $ evaluate =<< FS.submitIO hasBlockIO ioops)
111120
-- When IO result have been collected, intra-page lookups searches
112121
-- through the raw bytes (representing a disk page) for the lookup
113122
-- key. Again, we use perRunEnv here because IOOps contain mutable
@@ -117,20 +126,21 @@ benchLookups conf@Config{name} =
117126
-- only compute WHNF.
118127
, bench "Perform intra-page lookups" $
119128
perRunEnvWithCleanup
120-
( newArena arenaManager >>= \arena ->
121-
stToIO (prepLookups arena blooms indexes kopsFiles ks) >>= \(rkixs, ioops) ->
122-
FS.submitIO hasBlockIO ioops >>= \ioress ->
123-
pure (rkixs, ioops, ioress, arena)
124-
)
125-
(\(_, _, _, arena) -> closeArena arenaManager arena) $ \ ~(rkixs, ioops, ioress, _) -> do
126-
!_ <- intraPageLookups resolveV rs ks rkixs ioops ioress
127-
pure ()
129+
(do (rkixs, ioops, pages) <-
130+
unmanagedPreparedLookups pagealloc blooms
131+
indexes kopsFiles ks
132+
ioress <- FS.submitIO hasBlockIO ioops
133+
return ((rkixs, ioops, pages), ioress))
134+
(cleanupPreparedLookups pagealloc . fst)
135+
(\ ~((rkixs, ioops, _pages), ioress) ->
136+
void $ evaluate =<< intraPageLookups resolveV rs ks
137+
rkixs ioops ioress)
128138
-- The whole shebang: lookup preparation, doing the IO, and then
129139
-- performing intra-page-lookups. Again, we evaluate the result to
130140
-- WHNF because it is the same result that intraPageLookups produces
131141
-- (see above).
132142
, bench "Lookups in IO" $
133-
whnfAppIO (\ks' -> lookupsIO hasBlockIO arenaManager resolveV rs blooms indexes kopsFiles ks') ks
143+
whnfAppIO (\ks' -> lookupsIO hasBlockIO pagealloc resolveV rs blooms indexes kopsFiles ks') ks
134144
]
135145
where
136146
withEnv = envWithCleanup
@@ -162,14 +172,14 @@ data Config = Config {
162172
lookupsInBatchesEnv ::
163173
Config
164174
-> IO ( FilePath -- ^ Temporary directory
165-
, ArenaManager RealWorld
175+
, PageAlloc RealWorld
166176
, FS.HasFS IO FS.HandleIO
167177
, FS.HasBlockIO IO FS.HandleIO
168178
, V.Vector (Run (FS.Handle FS.HandleIO))
169179
, V.Vector SerialisedKey
170180
)
171181
lookupsInBatchesEnv Config {..} = do
172-
arenaManager <- newArenaManager
182+
pagealloc <- newPageAlloc
173183
sysTmpDir <- getCanonicalTemporaryDirectory
174184
benchTmpDir <- createTempDirectory sysTmpDir "lookupsInBatchesEnv"
175185
(storedKeys, lookupKeys) <- lookupsEnv (mkStdGen 17) nentries npos nneg
@@ -184,7 +194,7 @@ lookupsInBatchesEnv Config {..} = do
184194
assert (npagesReal * 42 <= nentriesReal) $ pure ()
185195
assert (npagesReal * 43 >= nentriesReal) $ pure ()
186196
pure ( benchTmpDir
187-
, arenaManager
197+
, pagealloc
188198
, hasFS
189199
, hasBlockIO
190200
, V.singleton r
@@ -193,14 +203,14 @@ lookupsInBatchesEnv Config {..} = do
193203

194204
lookupsInBatchesCleanup ::
195205
( FilePath -- ^ Temporary directory
196-
, ArenaManager RealWorld
206+
, PageAlloc RealWorld
197207
, FS.HasFS IO FS.HandleIO
198208
, FS.HasBlockIO IO FS.HandleIO
199209
, V.Vector (Run (FS.Handle FS.HandleIO))
200210
, V.Vector SerialisedKey
201211
)
202212
-> IO ()
203-
lookupsInBatchesCleanup (tmpDir, _arenaManager, hasFS, hasBlockIO, rs, _) = do
213+
lookupsInBatchesCleanup (tmpDir, _pagealloc, hasFS, hasBlockIO, rs, _) = do
204214
FS.close hasBlockIO
205215
forM_ rs $ Run.removeReference hasFS
206216
removeDirectoryRecursive tmpDir

cabal.project

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ benchmarks: True
3030
constraints: bloomfilter <0
3131

3232
-- comment me if you are benchmarking
33-
import: cabal.project.debug
33+
--import: cabal.project.debug
3434

3535
-- comment me if you don't have liburing installed
3636
--

lsm-tree.cabal

+2-3
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@ library
5151
hs-source-dirs: src
5252
exposed-modules:
5353
Control.Concurrent.Class.MonadSTM.RWVar
54-
Data.Arena
5554
Data.Map.Range
5655
Database.LSMTree.Common
5756
Database.LSMTree.Internal
@@ -73,6 +72,7 @@ library
7372
Database.LSMTree.Internal.Normal
7473
Database.LSMTree.Internal.PageAcc
7574
Database.LSMTree.Internal.PageAcc1
75+
Database.LSMTree.Internal.PageAlloc
7676
Database.LSMTree.Internal.Paths
7777
Database.LSMTree.Internal.Primitive
7878
Database.LSMTree.Internal.Range
@@ -233,7 +233,6 @@ test-suite lsm-tree-test
233233
Database.LSMTree.ModelIO.Normal
234234
Database.LSMTree.ModelIO.Session
235235
Test.Control.Concurrent.Class.MonadSTM.RWVar
236-
Test.Data.Arena
237236
Test.Database.LSMTree.Class.Monoidal
238237
Test.Database.LSMTree.Class.Normal
239238
Test.Database.LSMTree.Generators
@@ -247,6 +246,7 @@ test-suite lsm-tree-test
247246
Test.Database.LSMTree.Internal.Monkey
248247
Test.Database.LSMTree.Internal.PageAcc
249248
Test.Database.LSMTree.Internal.PageAcc1
249+
Test.Database.LSMTree.Internal.PageAlloc
250250
Test.Database.LSMTree.Internal.RawOverflowPage
251251
Test.Database.LSMTree.Internal.RawPage
252252
Test.Database.LSMTree.Internal.Run
@@ -394,7 +394,6 @@ benchmark lsm-tree-bench-lookups
394394
, bytestring
395395
, deepseq
396396
, fs-api
397-
, io-classes
398397
, lsm-tree
399398
, lsm-tree:blockio-api
400399
, lsm-tree:bloomfilter

0 commit comments

Comments
 (0)