
Am 28.09.2011 14:05, schrieb Yves Parès:
Interesting, so what have you used to get that speedup? A monad stack of ContT and State (*)? Just the Cont monad?
This is a module with a state monad transformer that I used before (the name STPlus is misleading - and sorry for the long email): {-# LANGUAGE RankNTypes, MultiParamTypeClasses, FlexibleInstances #-} module Search.SearchMonad ( STPlus, return, (>>=), get, put, gets, modify, lift, liftIO, runSearch, execSearch ) where import Control.Monad import Control.Monad.State hiding (lift, gets, modify) newtype STPlus s m a = STPlus { runSTPlus :: s -> m (a, s) } {-# INLINE runSTPlus #-} instance Monad m => Monad (STPlus s m) where {-# INLINE return #-} return v = STPlus (\s -> return (v, s)) {-# INLINE (>>=) #-} (>>=) = bindSTPlus {-# INLINE bindSTPlus #-} bindSTPlus :: Monad m => STPlus s m a -> (a -> STPlus s m b) -> STPlus s m b bindSTPlus ms f = STPlus $ \s -> case runSTPlus ms s of m -> m >>= \(v', s') -> case f v' of fv -> runSTPlus fv s' instance Monad m => MonadState s (STPlus s m) where {-# INLINE get #-} get = STPlus $ \s -> return (s, s) {-# INLINE put #-} put s = STPlus $ \_ -> return ((), s) instance MonadIO m => MonadIO (STPlus s m) where {-# INLINE liftIO #-} liftIO = lift . liftIO runSearch :: Monad m => STPlus s m a -> s -> m (a, s) runSearch = runSTPlus execSearch ms s = liftM snd $ runSearch ms s {-# INLINE lift #-} lift :: Monad m => m a -> STPlus s m a lift m = STPlus $ \s -> m >>= \v -> return (v, s) {-# INLINE gets #-} gets :: Monad m => (s -> a) -> STPlus s m a -- gets f = STPlus $ \s -> return (f s, s) gets f = STPlus $ \s -> case f s of fs -> return (fs, s) {-# INLINE modify #-} modify :: Monad m => (s -> s) -> STPlus s m () modify f = STPlus $ \s -> case f s of fs -> return ((), fs) And this is how the module looks now: {-# LANGUAGE RankNTypes, MultiParamTypeClasses, FlexibleInstances #-} module Search.SearchMonadCPS ( STPlus, return, (>>=), get, put, gets, modify, lift, liftIO, runSearch, execSearch ) where import Control.Monad import Control.Monad.State hiding (lift, gets, modify) newtype STPlus r s m a = STPlus { runSTPlus :: s -> (a -> s -> m r) -> m r } instance Monad (STPlus r s m) where return a = STPlus $ \s k -> k a s c >>= f = STPlus $ \s0 k -> runSTPlus c s0 $ \a s1 -> runSTPlus (f a) s1 k instance MonadState s (STPlus r s m) where get = STPlus $ \s k -> k s s put s = STPlus $ \_ k -> k () s instance MonadIO m => MonadIO (STPlus r s m) where {-# INLINE liftIO #-} liftIO = lift . liftIO runSearch :: Monad m => STPlus (a, s) s m a -> s -> m (a, s) runSearch c s = runSTPlus c s $ \a s0 -> return (a, s0) execSearch ms s = liftM snd $ runSearch ms s {-# INLINE lift #-} lift :: Monad m => m a -> STPlus r s m a lift m = STPlus $ \s k -> m >>= \a -> k a s {-# INLINE gets #-} gets :: Monad m => (s -> a) -> STPlus r s m a gets f = STPlus $ \s k -> k (f s) s {-# INLINE modify #-} modify :: Monad m => (s -> s) -> STPlus r s m () modify f = STPlus $ \s k -> k () (f s) And then I have (in different modules): Client code (starting an PV search to a given depth): type CtxIO = ReaderT Context IO bestMoveCont :: Int -> MyState -> Maybe Int -> [Move] -> [Move] -> CtxIO IterResult bestMoveCont ... = do ... ((sc, path, rmvsf), statf) <- runSearch (alphaBeta abc) stati ... Search framework: class Monad m => Node m where staticVal :: m Int -- static evaluation of a node materVal :: m Int -- material evaluation (for prune purpose) genEdges :: Int -> Int -> Bool -> m ([Move], [Move]) -- generate all legal edges genTactEdges :: m [Move] -- generate all edges in tactical positions ... type Search m a = forall r. STPlus r PVState m a alphaBeta :: Node m => ABControl -> m (Int, [Move], [Move]) alphaBeta abc = do let !d = maxdepth abc rmvs = Alt $ rootmvs abc lpv = Seq $ lastpv abc searchReduced a b = pvRootSearch a b d lpv rmvs True searchFull = pvRootSearch salpha0 sbeta0 d lpv rmvs False r <- if useAspirWin ... pvRootSearch :: Node m => Int -> Int -> Int -> Seq Move -> Alt Move -> Bool -> Search m (Int, Seq Move, Alt Move) ... And then the chess specific implementation of the game state in another module: type Game r m = STPlus r MyState m ... instance CtxMon m => Node (Game r m) where staticVal = staticVal0 materVal = materVal0 genEdges = genMoves ... genMoves :: CtxMon m => Int -> Int -> Bool -> Game r m ([Move], [Move]) genMoves depth absdp pv = do Nicu
(*) If so, were you using the strict version of State?
Would it be possible to see the differences between the 2 versions of you code?
2011/9/27 Nicu Ionita
mailto:nicu.ionita@acons.at> Hello list,
Starting from this emails (http://web.archiveorange.com/archive/v/nDNOvSM4JT3GJRSjOm9P) I could refactor my code (a UCI chess engine, with complex functions, in which the search has a complex monad stack) to run twice as fast as with even some hand unroled state transformer! So from 23-24 kilo nodes per second it does now 45 to 50 kNps! And it looks like there is still some improvement room (I have to play a little bit with strictness annotations and so on).
(Previously I tried specializations, then I removed a lot of polimorphism, but nothing helped, it was like hitting a wall.)
Even more amazingly is that I could program it although I cannot really understand the Cont & ContT, but just taking the code example from Ryan Ingram (newtype ContState r s a = ...) and looking a bit at the code from ContT (from the transformers library), and after fixing some compilation errors, it worked and was so fast.
I wonder why the transformers library does not use this kind of state monad definition. Or does it, and what I got is just because of the unrolling? Are there monad (transformers) libraries which are faster? I saw the library kan-extensions but I did not understand (yet) how to use it.
Nicu
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org mailto:Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe