Skip to content

Commit 783ded7

Browse files
authored
Merge pull request #765 from IntersectMBO/jdral/blockio-0.1.0.0
Release `blockio-0.1.0.0`
2 parents 099df9e + 359f03c commit 783ded7

File tree

28 files changed

+869
-320
lines changed

28 files changed

+869
-320
lines changed

app/Database/LSMTree/Demo.hs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ import qualified System.FS.API as FS
2828
import qualified System.FS.BlockIO.API as FS
2929
import qualified System.FS.BlockIO.IO as FS
3030
import qualified System.FS.BlockIO.Sim as FSSim
31-
import qualified System.FS.IO as FS
3231
import qualified System.FS.Sim.MockFS as FSSim
3332
import System.IO.Unsafe (unsafePerformIO)
3433

@@ -151,8 +150,7 @@ demo = do
151150
print' (fmap getValue os)
152151

153152
do
154-
let hasFS = FS.ioHasFS (FS.MountPoint "")
155-
FS.withIOHasBlockIO hasFS FS.defaultIOCtxParams $ \hasBlockIO -> do
153+
FS.withIOHasBlockIO (FS.MountPoint "") FS.defaultIOCtxParams $ \hasFS hasBlockIO -> do
156154
simpleAction hasFS hasBlockIO
157155
pause -- [16]
158156

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

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -310,11 +310,10 @@ totalNumEntriesSanityCheck l1 runSizes =
310310
withFS ::
311311
(FS.HasFS IO FS.HandleIO -> FS.HasBlockIO IO FS.HandleIO -> IO a)
312312
-> IO a
313-
withFS action = do
314-
let hfs = FS.ioHasFS (FS.MountPoint "_bench_lookups")
315-
exists <- FS.doesDirectoryExist hfs (FS.mkFsPath [""])
316-
unless exists $ error ("_bench_lookups directory does not exist")
317-
FS.withIOHasBlockIO hfs FS.defaultIOCtxParams $ \hbio ->
313+
withFS action =
314+
FS.withIOHasBlockIO (FS.MountPoint "_bench_lookups") FS.defaultIOCtxParams $ \hfs hbio -> do
315+
exists <- FS.doesDirectoryExist hfs (FS.mkFsPath [""])
316+
unless exists $ error ("_bench_lookups directory does not exist")
318317
action hfs hbio
319318

320319
-- | Input environment for benchmarking lookup functions.

bench/macro/utxo-bench.hs

Lines changed: 15 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,7 @@ import qualified Options.Applicative as O
6363
import Prelude hiding (lookup)
6464
import qualified System.Clock as Clock
6565
import qualified System.FS.API as FS
66-
import qualified System.FS.BlockIO.API as FS
6766
import qualified System.FS.BlockIO.IO as FsIO
68-
import qualified System.FS.IO as FsIO
6967
import System.IO
7068
import System.Mem (performMajorGC)
7169
import qualified System.Random as Random
@@ -438,17 +436,8 @@ doSetup gopts opts = do
438436
void $ timed_ $ doSetup' gopts opts
439437

440438
doSetup' :: GlobalOpts -> SetupOpts -> IO ()
441-
doSetup' gopts opts = do
442-
let mountPoint :: FS.MountPoint
443-
mountPoint = FS.MountPoint (rootDir gopts)
444-
445-
let hasFS :: FS.HasFS IO FsIO.HandleIO
446-
hasFS = FsIO.ioHasFS mountPoint
447-
448-
hasBlockIO <- FsIO.ioHasBlockIO hasFS FS.defaultIOCtxParams
449-
450-
let name = LSM.toSnapshotName ("bench_" ++ show (initialSize gopts))
451-
439+
doSetup' gopts opts =
440+
FsIO.withIOHasBlockIO mountPoint FsIO.defaultIOCtxParams $ \hasFS hasBlockIO ->
452441
LSM.withOpenSession (mkTracer gopts) hasFS hasBlockIO benchSalt (FS.mkFsPath []) $ \session -> do
453442
tbl <- LSM.newTableWith @IO @K @V @B (mkTableConfigSetup gopts opts benchTableConfig) session
454443

@@ -462,6 +451,12 @@ doSetup' gopts opts = do
462451
]
463452

464453
LSM.saveSnapshot name label tbl
454+
where
455+
mountPoint :: FS.MountPoint
456+
mountPoint = FS.MountPoint (rootDir gopts)
457+
458+
name = LSM.toSnapshotName ("bench_" ++ show (initialSize gopts))
459+
465460

466461
-------------------------------------------------------------------------------
467462
-- dry-run
@@ -600,17 +595,8 @@ toOperations lookups inserts = (batch1, batch2)
600595
-------------------------------------------------------------------------------
601596

602597
doRun :: GlobalOpts -> RunOpts -> IO ()
603-
doRun gopts opts = do
604-
let mountPoint :: FS.MountPoint
605-
mountPoint = FS.MountPoint (rootDir gopts)
606-
607-
let hasFS :: FS.HasFS IO FsIO.HandleIO
608-
hasFS = FsIO.ioHasFS mountPoint
609-
610-
hasBlockIO <- FsIO.ioHasBlockIO hasFS FS.defaultIOCtxParams
611-
612-
let name = LSM.toSnapshotName "bench"
613-
598+
doRun gopts opts =
599+
FsIO.withIOHasBlockIO mountPoint FsIO.defaultIOCtxParams $ \hasFS hasBlockIO ->
614600
LSM.withOpenSession (mkTracer gopts) hasFS hasBlockIO benchSalt (FS.mkFsPath []) $ \session ->
615601
withLatencyHandle $ \h -> do
616602
-- open snapshot
@@ -652,6 +638,11 @@ doRun gopts opts = do
652638

653639
let ops = batchCount opts * batchSize opts
654640
printf "Operations per second: %7.01f ops/sec\n" (fromIntegral ops / time)
641+
where
642+
mountPoint :: FS.MountPoint
643+
mountPoint = FS.MountPoint (rootDir gopts)
644+
645+
name = LSM.toSnapshotName ("bench_" ++ show (initialSize gopts))
655646

656647
-------------------------------------------------------------------------------
657648
-- sequential

bench/micro/Bench/Database/LSMTree.hs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -433,8 +433,7 @@ mkFiles ::
433433
mkFiles = do
434434
sysTmpDir <- getCanonicalTemporaryDirectory
435435
benchTmpDir <- createTempDirectory sysTmpDir "full"
436-
let hfs = FS.ioHasFS (FS.MountPoint benchTmpDir)
437-
hbio <- FS.ioHasBlockIO hfs FS.defaultIOCtxParams
436+
(hfs, hbio) <- FS.ioHasBlockIO (FS.MountPoint benchTmpDir) FS.defaultIOCtxParams
438437
pure (benchTmpDir, hfs, hbio)
439438

440439
cleanupFiles ::

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -191,8 +191,7 @@ lookupsInBatchesEnv Config {..} = do
191191
sysTmpDir <- getCanonicalTemporaryDirectory
192192
benchTmpDir <- createTempDirectory sysTmpDir "lookupsInBatchesEnv"
193193
(storedKeys, lookupKeys) <- lookupsEnv (mkStdGen 17) nentries npos nneg
194-
let hasFS = FS.ioHasFS (FS.MountPoint benchTmpDir)
195-
hasBlockIO <- FS.ioHasBlockIO hasFS (fromMaybe FS.defaultIOCtxParams ioctxps)
194+
(hasFS, hasBlockIO) <- FS.ioHasBlockIO (FS.MountPoint benchTmpDir) (fromMaybe FS.defaultIOCtxParams ioctxps)
196195
wbblobs <- WBB.new hasFS (FS.mkFsPath ["0.wbblobs"])
197196
wb <- WB.fromMap <$> traverse (traverse (WBB.addBlob hasFS wbblobs)) storedKeys
198197
let fsps = RunFsPaths (FS.mkFsPath []) (RunNumber 0)

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -373,8 +373,7 @@ mergeEnv ::
373373
mergeEnv config = do
374374
sysTmpDir <- getCanonicalTemporaryDirectory
375375
benchTmpDir <- createTempDirectory sysTmpDir "mergeEnv"
376-
let hasFS = FS.ioHasFS (FS.MountPoint benchTmpDir)
377-
hasBlockIO <- FS.ioHasBlockIO hasFS FS.defaultIOCtxParams
376+
(hasFS, hasBlockIO) <- FS.ioHasBlockIO (FS.MountPoint benchTmpDir) FS.defaultIOCtxParams
378377
runs <- randomRuns hasFS hasBlockIO config (mkStdGen 17)
379378
pure (benchTmpDir, hasFS, hasBlockIO, runs)
380379

blockio/README.md

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,49 @@
11
# blockio
22

3-
This packages defines an abstract interface for batched, asynchronous I\/O,
4-
for use with the abstract interface for file system I\/O defined by the
5-
[fs-api](https://hackage.haskell.org/package/fs-api) package.
3+
Perform batches of disk I/O operations. Performing batches of disk I/O can lead
4+
to performance improvements over performing each disk I/O operation
5+
individually. Performing batches of disk I/O *concurrently* can lead to an even
6+
bigger performance improvement depending on the implementation of batched I/O.
67

7-
The /sim/ sub-library of this package defines /simulated/ batched, asynchronous I\/O
8-
for use with the [fs-sim](https://hackage.haskell.org/package/fs-sim) package.
8+
The batched I/O functionality in the library is separated into an *abstract
9+
interface* and *implementations* of that abstract interface. The advantage of
10+
programming against an abstract interface is that code can be agnostic to the
11+
implementation of the interface, allowing implementations to be freely swapped
12+
out. The library provides multiple implementations of batched I/O:
13+
platform-dependent implementations using the *real* file system (using
14+
asynchronous I/O), and a simulated implementation for testing purposes.
15+
16+
See the `System.FS.BlockIO` module for an example of how to use the library.
17+
18+
On Linux systems the *real* implementation is backed by
19+
[blockio-uring](https://hackage.haskell.org/package/blockio-uring), a library
20+
for asynchronous I/O that achieves good performance when performing batches
21+
concurrently. On Windows and MacOS systems the *real* implementation currently
22+
simply performs each I/O operation sequentially, which should achieve about the
23+
same performance as using non-batched I/O, but the library could be extended
24+
with asynchronous I/O implementations for Windows and MacOS as well. The
25+
simulated implementation also performs each I/O operation sequentially.
26+
27+
As mentioned before, the batched I/O functionality is separated into an
28+
*abstract interface* and *implementations* of that abstract interface. The
29+
advantage of programming against an abstract interface is that code can be
30+
agnostic to the implementation of the interface. For example, we could run code
31+
in production using the real file system, but we could also run the same code in
32+
a testing environment using a simulated file system. We could even switch from a
33+
default implementation to a more performant implementation in production if the
34+
performant implementation is available. Lastly, the abstract interface allows us
35+
to program against the file system in a uniform manner across different
36+
platforms, i.e., operating systems.
37+
38+
The `blockio` library defines the abstract interface for batched I/O. The
39+
library is an extension of the
40+
[fs-api](https://hackage.haskell.org/package/fs-api) library, which defines an
41+
abstract interface for (basic) file system I/O. Both `blockio` and `fs-api`
42+
provide an implementation of their interfaces using the real file system in
43+
`IO`.
44+
45+
The `blockio:sim` sub-library defines an implementation of the abstract
46+
interface from `blockio` that *simulates* batched I/O. This sub-library is an
47+
extension of the [fs-sim](https://hackage.haskell.org/package/fs-sim) library,
48+
which defines an implementation of the abstract interface from `fs-api` that
49+
simulates (basic) file system I/O.

blockio/blockio.cabal

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,23 @@
11
cabal-version: 3.4
22
name: blockio
33
version: 0.1.0.0
4-
synopsis: Abstract interface for batched, asynchronous I/O
4+
synopsis: Perform batches of disk I/O operations.
55
description:
6-
This packages defines an abstract interface for batched, asynchronous I\/O,
7-
for use with the abstract interface for file system I\/O defined by the
8-
[fs-api](https://hackage.haskell.org/package/fs-api) package.
9-
10-
The /sim/ sub-library of this package defines /simulated/ batched, asynchronous I\/O
11-
for use with the [fs-sim](https://hackage.haskell.org/package/fs-sim) package.
6+
Perform batches of disk I\/O operations. Performing batches of disk I\/O can
7+
lead to performance improvements over performing each disk I\/O operation
8+
individually. Performing batches of disk I\/O /concurrently/ can lead to an
9+
even bigger performance improvement depending on the implementation of batched
10+
I\/O.
11+
12+
The batched I\/O functionality in the library is separated into an /abstract/
13+
/interface/ and /implementations/ of that abstract interface. The advantage of
14+
programming against an abstract interface is that code can be agnostic to the
15+
implementation of the interface, allowing implementations to be freely swapped
16+
out. The library provides multiple implementations of batched I\/O:
17+
platform-dependent implementations using the /real/ file system (with
18+
asynchronous I\/O), and a simulated implementation for testing purposes.
19+
20+
See the "System.FS.BlockIO" module for an example of how to use the library.
1221

1322
license: Apache-2.0
1423
license-files:
@@ -18,7 +27,7 @@ license-files:
1827
author:
1928
Duncan Coutts, Joris Dral, Matthias Heinzel, Wolfgang Jeltsch, Wen Kokke, and Alex Washburn
2029

21-
maintainer: TODO: MAINTAINER EMAIL
30+
maintainer: [email protected]
2231
copyright:
2332
(c) 2023 Input Output Global, Inc. (IOG)
2433
(c) 2023-2025 INTERSECT
@@ -33,13 +42,11 @@ source-repository head
3342
location: https://github.com/IntersectMBO/lsm-tree
3443
subdir: blockio
3544

36-
-- TODO: this tag obviously does not exist yet because the package has not
37-
-- been published
3845
source-repository this
3946
type: git
4047
location: https://github.com/IntersectMBO/lsm-tree
41-
tag: blockio-0.1.0.0
4248
subdir: blockio
49+
tag: blockio-0.1.0.0
4350

4451
common warnings
4552
ghc-options:
@@ -67,8 +74,13 @@ library
6774
import: language, warnings
6875
hs-source-dirs: src
6976
exposed-modules:
77+
System.FS.BlockIO
7078
System.FS.BlockIO.API
7179
System.FS.BlockIO.IO
80+
System.FS.BlockIO.Serial.Internal
81+
82+
other-modules:
83+
System.FS.BlockIO.IO.Internal
7284
System.FS.BlockIO.Serial
7385

7486
build-depends:
@@ -113,7 +125,7 @@ test-suite test
113125
, bytestring
114126
, fs-api
115127
, primitive
116-
, QuickCheck ^>=2.15.0.1
128+
, QuickCheck >=2.15.0.1
117129
, tasty
118130
, tasty-hunit
119131
, tasty-quickcheck
@@ -128,12 +140,12 @@ library sim
128140
hs-source-dirs: src-sim
129141
exposed-modules: System.FS.BlockIO.Sim
130142
build-depends:
131-
, base >=4.16 && <4.22
143+
, base >=4.16 && <4.22
132144
, blockio
133-
, bytestring ^>=0.11.4.0 || ^>=0.12.1.0
145+
, bytestring ^>=0.11 || ^>=0.12
134146
, fs-api ^>=0.4
135147
, fs-sim ^>=0.4
136-
, io-classes ^>=1.6 || ^>=1.7 || ^>=1.8.0.1
148+
, io-classes ^>=1.6 || ^>=1.7 || ^>=1.8.0.1
137149
, io-classes:strict-stm
138150
, primitive ^>=0.9
139151

blockio/src-linux/System/FS/BlockIO/Async.hs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import System.FS.API (BufferOffset (..), FsErrorPath, FsPath,
1616
import qualified System.FS.BlockIO.API as API
1717
import System.FS.BlockIO.API (IOOp (..), IOResult (..), LockMode,
1818
ioopHandle)
19+
import qualified System.FS.BlockIO.IO.Internal as IOI
1920
import System.FS.IO (HandleIO)
2021
import System.FS.IO.Handle
2122
import qualified System.IO.BlockIO as I
@@ -32,7 +33,7 @@ asyncHasBlockIO ::
3233
-> (FsPath -> IO ())
3334
-> (FsPath -> FsPath -> IO ())
3435
-> HasFS IO HandleIO
35-
-> API.IOCtxParams
36+
-> IOI.IOCtxParams
3637
-> IO (API.HasBlockIO IO HandleIO)
3738
asyncHasBlockIO hSetNoCache hAdvise hAllocate tryLockFile hSynchronise synchroniseDirectory createHardLink hasFS ctxParams = do
3839
ctx <- I.initIOCtx (ctxParamsConv ctxParams)
@@ -48,8 +49,8 @@ asyncHasBlockIO hSetNoCache hAdvise hAllocate tryLockFile hSynchronise synchroni
4849
, API.createHardLink
4950
}
5051

51-
ctxParamsConv :: API.IOCtxParams -> I.IOCtxParams
52-
ctxParamsConv API.IOCtxParams{API.ioctxBatchSizeLimit, API.ioctxConcurrencyLimit} =
52+
ctxParamsConv :: IOI.IOCtxParams -> I.IOCtxParams
53+
ctxParamsConv IOI.IOCtxParams{IOI.ioctxBatchSizeLimit, IOI.ioctxConcurrencyLimit} =
5354
I.IOCtxParams {
5455
I.ioctxBatchSizeLimit = ioctxBatchSizeLimit
5556
, I.ioctxConcurrencyLimit = ioctxConcurrencyLimit
@@ -72,7 +73,7 @@ submitIO hasFS ioctx ioops = do
7273
-- the exception might change between versions of @blockio-uring@.
7374
-- Nonetheless, it's better than nothing.
7475
if isResourceVanishedError e && ioe_location e == "IOCtx closed"
75-
then throwIO (API.mkClosedError (SomeHasFS hasFS) "submitIO")
76+
then throwIO (IOI.mkClosedError (SomeHasFS hasFS) "submitIO")
7677
else throwIO e
7778

7879
rethrowErrno ::

blockio/src-linux/System/FS/BlockIO/Internal.hs

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,8 @@ module System.FS.BlockIO.Internal (
66

77
import qualified System.FS.API as FS
88
import System.FS.API (FsPath, Handle (..), HasFS)
9-
import qualified System.FS.BlockIO.API as FS
10-
import System.FS.BlockIO.API (Advice (..), FileOffset, HasBlockIO,
11-
IOCtxParams)
9+
import System.FS.BlockIO.API (Advice (..), FileOffset, HasBlockIO)
10+
import qualified System.FS.BlockIO.IO.Internal as IOI
1211
import System.FS.IO (HandleIO)
1312
import qualified System.FS.IO.Handle as FS
1413
import qualified System.Posix.Fcntl as Fcntl
@@ -23,29 +22,29 @@ import qualified System.FS.BlockIO.Async as Async
2322

2423
ioHasBlockIO ::
2524
HasFS IO HandleIO
26-
-> IOCtxParams
25+
-> IOI.IOCtxParams
2726
-> IO (HasBlockIO IO HandleIO)
2827
#if SERIALBLOCKIO
2928
ioHasBlockIO hfs _params =
3029
Serial.serialHasBlockIO
3130
hSetNoCache
3231
hAdvise
3332
hAllocate
34-
(FS.tryLockFileIO hfs)
33+
(IOI.tryLockFileIO hfs)
3534
hSynchronise
3635
(synchroniseDirectory hfs)
37-
(FS.createHardLinkIO hfs Unix.createLink)
36+
(IOI.createHardLinkIO hfs Unix.createLink)
3837
hfs
3938
#else
4039
ioHasBlockIO hfs params =
4140
Async.asyncHasBlockIO
4241
hSetNoCache
4342
hAdvise
4443
hAllocate
45-
(FS.tryLockFileIO hfs)
44+
(IOI.tryLockFileIO hfs)
4645
hSynchronise
4746
(synchroniseDirectory hfs)
48-
(FS.createHardLinkIO hfs Unix.createLink)
47+
(IOI.createHardLinkIO hfs Unix.createLink)
4948
hfs
5049
params
5150
#endif

0 commit comments

Comments
 (0)