-
Notifications
You must be signed in to change notification settings - Fork 7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Swapping IO for abstract IO type-classes #396
base: main
Are you sure you want to change the base?
Swapping IO for abstract IO type-classes #396
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would like some commentary from reviewers more experience balancing
SPECIALISE
directive usage as to whether the abundant and boilerplate additions of theSPECIALISE
directives are a wise decision.
Generalising the code to work for any IO-like monad is done for testing purposes only, and since the library is intended to be highly performant, the generalisation should ideally have no overhead. So, the SPECIALISE
pragmas are warranted -- the compile time is also not prohibitively long at the moment, even with the changes in this PR
On that note, we should verify that the generalisation does not impact our performance. Let's run the benchmarks to see if we can see a clear difference
Also, I have some proposed changes on the branch jdral/io-classes-for-abstract-types
. You can cherry-pick what you want to include.
It's puzzling that tests are currently timing out -- there are no logic changes AFAICS. We should find out what is happening here
Note that with the changes on my branch, it seems most of our tests succeed that are currently failing, except for propLockstepIO_RealImpl_RealFS
. However, I'm hesitant to say that my changes "fix" the problem if we don't know the origin of the bug. However, I've played around a bit with the statemachine tests, and it seems that disabling the Open
action makes the propLockstepIO_RealImpl_RealFS
pass... Not sure why
{-# INLINE withOpenSession #-} | ||
{-# SPECIALISE withOpenSession :: Session IO h -> (SessionEnv IO h -> IO a) -> IO a #-} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With an INLINE
, the SPECIALISE
is probably not necessary, but it doesn't hurt to include it anyway
openFromDisk :: | ||
forall m h. | ||
(MonadFix m, MonadSTM m, MonadThrow m, PrimMonad m) | ||
=> HasFS m h |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
openFromDisk :: | |
forall m h. | |
(MonadFix m, MonadSTM m, MonadThrow m, PrimMonad m) | |
=> HasFS m h | |
openFromDisk :: | |
forall m h. | |
(MonadFix m, MonadSTM m, MonadThrow m, PrimMonad m) | |
=> HasFS m h |
import Control.Monad.Primitive | ||
import qualified Control.Monad.ST as ST | ||
--import qualified Control.Monad.ST as ST |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
--import qualified Control.Monad.ST as ST |
@@ -140,18 +150,20 @@ addLargeSerialisedKeyOp fs builder@RunBuilder{runBuilderAcc} key page overflowPa | |||
writeRawOverflowPages fs builder overflowPages' | |||
for_ chunks $ writeIndexChunk fs builder | |||
|
|||
{-# SPECIALISE unsafeFinalise :: HasFS IO h -> HasBlockIO IO h -> Bool -> RunBuilder (PrimState IO) (FS.Handle h) -> IO (RunFsPaths, Bloom SerialisedKey, IndexCompact, NumEntries) #-} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use RealWorld
instead of PrimState IO
.
{-# SPECIALISE unsafeFinalise :: HasFS IO h -> HasBlockIO IO h -> Bool -> RunBuilder (PrimState IO) (FS.Handle h) -> IO (RunFsPaths, Bloom SerialisedKey, IndexCompact, NumEntries) #-} | |
{-# SPECIALISE unsafeFinalise :: HasFS IO h -> HasBlockIO IO h -> Bool -> RunBuilder RealWorld (FS.Handle h) -> IO (RunFsPaths, Bloom SerialisedKey, IndexCompact, NumEntries) #-} |
Make sure to change every other occurrence in this PR as well
-- | Close a run that is being constructed (has not been finalised yet), | ||
-- removing all files associated with it from disk. | ||
-- After calling this operation, the run must not be used anymore. | ||
-- | ||
-- TODO: Ensure proper cleanup even in presence of exceptions. | ||
close :: HasFS IO h -> RunBuilder RealWorld (FS.Handle h) -> IO () | ||
close :: (MonadSTM m) => HasFS m h -> RunBuilder (PrimState m) (FS.Handle h) -> m () |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
close :: (MonadSTM m) => HasFS m h -> RunBuilder (PrimState m) (FS.Handle h) -> m () | |
close :: MonadSTM m => HasFS m h -> RunBuilder (PrimState m) (FS.Handle h) -> m () |
Helpers | ||
-------------------------------------------------------------------------------} | ||
|
||
writeRawPage :: HasFS IO h -> RunBuilder RealWorld (FS.Handle h) -> RawPage -> IO () | ||
{-# SPECIALISE writeRawPage :: HasFS IO h -> RunBuilder (PrimState IO) (FS.Handle h) -> RawPage -> IO () #-} | ||
writeRawPage :: (MonadSTM m, PrimMonad m) => HasFS m h -> RunBuilder (PrimState m) (FS.Handle h) -> RawPage -> m () |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's wrap the function signatures for helpers in this section over multiple lines if they far exceed 80 characters in width
@@ -52,7 +56,7 @@ import System.FS.BlockIO.API (HasBlockIO) | |||
data RunReader m fhandle = RunReader { | |||
-- | The disk page currently being read. If it is 'Nothing', the reader | |||
-- is considered closed. | |||
readerCurrentPage :: !(IORef (Maybe RawPage)) | |||
readerCurrentPage :: !(STRef (PrimState m) (Maybe RawPage)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Try a MutVar
instead
-- | Returns 'Nothing' on EOF. | ||
readDiskPage :: HasFS IO h -> FS.Handle h -> IO (Maybe RawPage) | ||
readDiskPage :: (MonadCatch m, MonadSTM m, PrimMonad m) => HasFS m h -> FS.Handle h -> m (Maybe RawPage) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
readDiskPage :: (MonadCatch m, MonadSTM m, PrimMonad m) => HasFS m h -> FS.Handle h -> m (Maybe RawPage) | |
readDiskPage :: (MonadCatch m, PrimMonad m) => HasFS m h -> FS.Handle h -> m (Maybe RawPage) |
00ddaee
to
52875ff
Compare
Refactor to use
io-classes
type-classes instead ofIO
A reasonably large-scale refactoring (of API type signatures) which addresses the numerous TODO annotations stating "replace by
io-classes
constraints for IO simulation" in order to provide a more general, polymorphic API for users of thelsm-tree
library. The indirect object to be replaced that the TODO annotation references are them ~ IO
constraints found on the same line(s) prefixing the TODO annotation, allowing the post-refactoring functions to abstract overIO
operations rather than exposing an API which is specialized toIO
. While most TODO annotations were in theDatabase.LSMTree.Internal
module, this PR touches many additional modules. This wide footprint of changes is necessary because theDatabase.LSMTree.Internal
module imports many other modules from thelsm-tree
project, hence each of these "dependency modules" required a "IO-abstraction" API update in order to facilitate the "IO-abstraction" of theDatabase.LSMTree.Internal
module.The
io-classes
which are utilized in place of specializedIO
functions are:MonadCatch
MonadMask
MonadMVar
MonadSTM
MonadST
MonadThrow
Important Note
Wherever the PR updated a module API from exposing a specialized
IO
version of a function to a generalizedio-classes
function, aSPECIALISE
directive was also added which specializes the polymorphicm
type parameter toIO
. The rational here is to have the module's interface file still expose a version of the function which is specialized toIO
in order to prevent any performance regressions. However, this may be excessive and result in unnecessarily longer compiler times and code sizes.I would like some commentary from reviewers more experience balancing
SPECIALISE
directive usage as to whether the abundant and boilerplate additions of theSPECIALISE
directives are a wise decision.