ã¹ããªã¼ã å¦çã©ã¤ãã©ãªã¯Haskellã«ããã¦ç«¶äºã®æ¿ããåéã®ä¸ã¤ã ãã¹ããªã¼ã å¦çã©ã¤ãã©ãªã¨ã¯å¤§éæã«è¨ãã¨ãIOãªã©ã®ä½ç¨ã絡ããªããå¤ã®å(ã¹ããªã¼ã )ãçæããããå¦çãããããæ§é ãæä¾ããã©ã¤ãã©ãªã§ãããå¤ãã®ã©ã¤ãã©ãªã¯ã以ä¸ã®3種ã®æ§é ãå®ç¾©ãã¦ããã
- çç£è (ãããã¥ã¼ãµã¼): IOãªã©ã®ã¢ã¯ã·ã§ã³ãä¼´ãã¤ã¤å¤ãçæããã
- æ¶è²»è
(ã³ã³ã·ã¥ã¼ãã¼): å¤ãã®å ´åã¢ããå¤æåã«ãªã£ã¦ããã
await :: Consumer s m s
ã®ãããªã¢ã¯ã·ã§ã³ãçµã¿åãããå¤ã®åãå¦çããããã°ã©ã ãæ¸ããã - å¤æè (ãã©ã³ã¹ãã¥ã¼ãµã¼): å ¥åãåãåããªãããåºåãã§ããã
çç£è ã¨æ¶è²»è ãå¤æè ã®ç¹æ®ãªå ´åã§ãããã®ãå¤ãã
ä»åã¯ãåºæ¬ä¸ã®åºæ¬ã¨ãè¨ããæä½ã§ããã¹ãã£ã³ã®éãã調ã¹ããscan (+) 0
ã¯å
¥åã¹ããªã¼ã [0,1,2,3, ...]
ãåºå[0,1,3,6, ...]
ã®ããã«å¤æããã
iteratee, tubes, streaming, machinecell, io-streams, pipes, machines, conduit, boomboxã¨è©¦ä½åã®feedersãpredatorsããã±ã¼ã¸ããã³ããã¼ã¯ãããã½ã¼ã¹ã³ã¼ãã¯https://github.com/fumieval/feeders/blob/all-bench/benchmarks/benchmark.hsã«ããã ã©ã¤ãã©ãªæ°ã¨ããç¹ã§ã¯ã2017å¹´ç¾å¨æãç¶²ç¾ çãªãã³ããã¼ã¯ã ããã
pipes
- 使ç¨å®ç¸¾: ghc-mod, purescript
- å©ç¹ éããããã¥ã¡ã³ããè±å¯
- æ¬ ç¹ çµç«¯ãæ®ä½ãæ±ããªã
ã¾ãã¯äººæ°ã®pipesãyieldã¨awaitãã¢ããã§çµã¿åãããç´ ç´ãªã¤ã³ã¿ã¼ãã§ã¤ã¹ãé
åã§ãscanã®å®è£
ãããããããã
ãã ããrunEffect
以å¤ã®æ¹æ³ã§ã®å解ã¯ãã¾ãæ³å®ãã¦ããªãã®ããèªåã§Pipe
ãå解ããã«ã¯Pipes.Internal
ã¢ã¸ã¥ã¼ã«ãã¤ã³ãã¼ãããªãã¨ãããªãããã®éã¯pipesã®è¨è¨ã®ç解ãå¿
é ã¨ãªãã
scan :: Monad m => (x -> a -> x) -> x -> (x -> b) -> Pipe a b m r scan step begin done = go begin where go x = do yield (done x) a <- await let x' = step x a go $! x'
å¤æã«ç¸å½ããå¤ã¨ããããèµ°ãããé¢æ°ã«åãã¦Criterionã§ãã³ããã¼ã¯ããã
sourceP :: Monad m => P.Producer Int m () sourceP = each [1..10000] drainP :: Pipe Int a Identity () -> () drainP p = runIdentity $ runEffect $ for (sourceP >-> p) discard main = defaultMain [ bench "pipes" $ whnf drainP (scan (+) 0 id) ]
10000è¦ç´ ãå¦çããã®ã«179μsã¨ããçµæãåºããä¸ä»¶ããã18ããç§ã¯ãªããªãæªããªãã¨è¨ããã ããããªãGHCã¯8.0.2ã§ãCPUã¯Intel(R) Core(TM) i7-6700K CPU @ 4.00GHzã§ããã
time 179.3 μs (177.9 μs .. 180.8 μs) 1.000 R² (0.999 R² .. 1.000 R²) mean 179.1 μs (178.4 μs .. 179.9 μs) std dev 2.457 μs (2.043 μs .. 3.124 μs)
tubes
- å©ç¹ ã¤ã³ã¿ã¼ãã§ã¤ã¹ã親ãã¿ããã
- æ¬ ç¹ æ¥µç«¯ã«é ã
time 22.99 s (22.21 s .. 24.75 s) 0.999 R² (0.999 R² .. 1.000 R²) mean 22.86 s (22.61 s .. 23.04 s) std dev 269.8 ms (0.0 s .. 308.6 ms)
23ãç§ãã¨ããå§åçãªæéãç®ãå¼ããã®ã¯tubesã ãFreeã¢ããããã¼ã¹ã«ããåºæ¬ãæ¼ãããAPIããªã¹ãã¢ããç¸å½ã®Source
ã«å ãContravariantãªSink
ã¨ç¬¬ä¸å°è±¡ã¯è¯ããããããã«10ä¸åãé
ãã¨å®ç¨çã¨ã¯è¨ããããã
scanT :: Monad m => (b -> a -> b) -> b -> Tube a b m x scanT f = go where go b = await >>= \x -> let !b' = f b x in yield b' >> go b' sourceT :: Monad m => Tube () Int m () sourceT = each [1..value] drainT :: Tube Int a Identity () -> () drainT h = runIdentity $ runTube $ sourceT >< h >< stop
streaming
使ç¨å®ç¸¾: ä¸æ
- å©ç¹ éã
- æ¬ ç¹ æ¶è²»è ããªã
benchmarking scan/streaming time 77.40 μs (77.07 μs .. 77.74 μs) 1.000 R² (1.000 R² .. 1.000 R²) mean 77.17 μs (77.01 μs .. 77.41 μs) std dev 668.9 ns (552.8 ns .. 820.2 ns)
streamingã¯pipesã®å以ä¸ã®é度ãå°è±¡çã ãstreamingã«ã¯å¤æè ãæ¶è²»è ã«ç¸å½ããæ§é ã¯ãªããããããä¸å ¬å¹³ãªæ¯è¼ãããããªãã
Stream (Of a)
ãaãçç£ããã¢ããå¤æåã§ãããããããªã¹ãã®ããã«æ±ãæ°ã
ã®é¢æ°ãæä¾ããã¦ããã
drainS :: (Stream (Of Int) Identity () -> Stream (Of a) Identity ()) -> () drainS h = runIdentity $ effects $ h sourceS sourceS :: Monad m => Stream (Of Int) m () sourceS = each [1..value] ... , bench "streaming" $ whnf drainS (S.scan (+) 0 id)
ã¹ããªã¼ã å¦çã©ã¤ãã©ãªã使ãåæ©ã¯ã¢ããã£ãã¯ãªæ¶è²»ã§ãããã¨ãå¤ãããããã§ãªãå ´åã¯é¸æè¢ã¨ãªãããã ããã
io-streams
使ç¨å®ç¸¾: snap
- å©ç¹ éã
- æ¬ ç¹ ä½ãæ¸ãã«ãIOã使ããªãã¨ãããªã
time 87.93 μs (86.58 μs .. 89.68 μs) 0.996 R² (0.989 R² .. 1.000 R²) mean 88.13 μs (87.08 μs .. 91.47 μs) std dev 5.351 μs (1.913 μs .. 10.55 μs) variance introduced by outliers: 63% (severely inflated)
io-streamsã¯ä½¿ãã¢ãããIOã«éå®ããã¨ããéãç´ã£ãè¨è¨ã®ããã±ã¼ã¸ã ããããããã£ã¦ããããstreamingã«è¿«ãé度ãåºã¦ããä¾®ããªãã
import qualified System.IO.Streams as Is drainIs :: (Is.InputStream Int -> IO (Is.InputStream b)) -> IO () drainIs h = do i <- Is.fromList [1..value] i' <- h i o <- Is.nullOutput Is.connect i' o scanIs :: (b -> a -> b) -> b -> Is.InputStream a -> IO (Is.InputStream b) scanIs f b0 i = do r <- newIORef b0 Is.makeInputStream $ Is.read i >>= \case Nothing -> return Nothing Just x -> do b <- readIORef r let !b' = f b x writeIORef r b' return $ Just b'
machines
使ç¨å®ç¸¾: ä¸æ
- å©ç¹ å種æ§é ãéæã§ãæ¡å¼µæ§ã«å¯ã
- æ¬ ç¹ TeeãStackãªã©ã®çºå±å½¢ã¯APIãä¹ããããã¾ãåæã§ããªã
ekmettçºã®machinesã¯ã¾ãã¾ãã®æ§è½ã ã
time 190.8 μs (190.1 μs .. 191.5 μs) 1.000 R² (1.000 R² .. 1.000 R²) mean 190.7 μs (190.2 μs .. 191.1 μs) std dev 1.542 μs (1.366 μs .. 1.771 μs)
PlanT
ã¨ããCPSã®ã¢ããããå¤æå¨ã®MachineT
ãé³é ããã¨ããã¢ããã¼ããç¨ãã¦ãããè¤æ°ã®å
¥åããµãã¼ããã¦ããã®ãé¢ç½ããæ¯è¼çç¿å¾ã¯å®¹æã ã奥ãæ·±ããã¾ããpipesã¨éãçµç«¯ã«å¯¾å¿ã§ããã
sourceM = enumerateFromTo 1 value drainM :: ProcessT Identity Int o -> () drainM m = runIdentity $ runT_ (sourceM ~> m) , bench "machines" $ whnf drainM (scan (+) 0)
conduit
使ç¨å®ç¸¾: Yesod
- å©ç¹ ã¹ããªã¼ã ã®çµç«¯ãæ®ãããã¡ãã¨æ±ãã
- æ¬ ç¹ APIãè¤éããªã¼ãã¼ãããããã
time 302.4 μs (301.5 μs .. 303.3 μs) 1.000 R² (1.000 R² .. 1.000 R²) mean 302.1 μs (301.6 μs .. 302.8 μs) std dev 2.029 μs (1.547 μs .. 2.515 μs)
ãã¤ã¦é»éæ代ãç¯ããconduitã¯machinesãããé ãã£ããããããã¹ããªã¼ã ã®çµç«¯åã³æ®ä½ããªã½ã¼ã¹ã®è§£æ¾ãªã©ããµãã¼ããã¦ãããã¨ãèããã°ããªãåªç§ã ã¨è¨ããã
import qualified Data.Conduit.List as C import qualified Data.Conduit.Combinators as CC drainC :: C.Conduit Int Identity a -> () drainC c = runIdentity $ (sourceC C.$= c) C.$$ C.sinkNull sourceC = C.enumFromTo 1 value , bench "conduit" $ whnf drainC (CC.scanl (+) 0)
ãã®æã®ã©ã¤ãã©ãªã«ä¾åããªããããªç°å¢ã®å¤åãèµ·ãã£ããã®ã®ãã¾ã ã¾ã ç¾å½¹ã ãã³ã³ããã¼ã¿ã®ç¨®é¡ãé常ã«å¤ãããã¼ãã«ãé«ãã¨ããé£ç¹ãããã
iteratee
- å©ç¹ ã¹ããªã¼ã ã®çµç«¯ãæ®ä½ã¯ãã¡ããã·ã¼ã¯ãªã©ã表ç¾å¯è½
- æ¬ ç¹ é ããè¨è¨ãæ±ããæ±ããé常ã«é£ãã
使ç¨å®ç¸¾: Tsuru Capital
æå¤åã®iterateeã¯pipesã®ç´20åé ãã¨ããæ®å¿µãªçµæã¨ãªã£ãã
time 3.392 ms (3.299 ms .. 3.502 ms) 0.995 R² (0.993 R² .. 0.998 R²) mean 3.361 ms (3.325 ms .. 3.399 ms) std dev 121.1 us (105.5 us .. 142.4 us) variance introduced by outliers: 20% (moderately inflated)
å®è£ ãããããããã¨ã¯è¨ãããããä»ããã¦ãã®ã©ã¤ãã©ãªãé¸æããå¿ è¦ã¯ãªãã ãããiterateeã使ã£ãã³ã¼ããä¿å®ããã®ã¯è¦è¡ãã®ãã®ã ã
import qualified Data.Iteratee.Iteratee as I import qualified Data.Iteratee.ListLike as I scanI :: Monad m => (b -> a -> b) -> b -> I.Enumeratee [a] [b] m x scanI f = I.unfoldConvStream (\a0 -> I.liftI $ \case I.Chunk xs -> return $ mapAccumL (\a x -> let !r = f a x in (r, r)) a0 xs I.EOF _ -> return (a0, [a0])) sourceI :: I.Enumerator [Int] Identity a sourceI = I.enumList $ map pure [1..value] drainI :: I.Enumeratee [Int] [a] Identity () -> () drainI h = runIdentity $ I.run $ runIdentity $ I.run $ runIdentity $ sourceI $ h $ I.mapM_ $ const $ return ()
feeders
- å©ç¹ iterateeã®åé¡ç¹ãå æãã親ãã¿ãããã¤ã³ã¿ã¼ãã§ã¤ã¹ãæã¤
- æ¬ ç¹ ã¾ã ã¾ã é ã
time 414.3 μs (409.7 μs .. 421.8 μs) 0.998 R² (0.996 R² .. 1.000 R²) mean 413.9 μs (412.1 μs .. 421.0 μs) std dev 9.814 μs (3.347 μs .. 22.63 μs) variance introduced by outliers: 15% (moderately inflated)
Feederã¯ããæ¶è²»è ã«ä¾çµ¦ããæ§é ãã¨ãã¦çç£è ã表ç¾ããiterateeã®èãæ¹ãç¶æ¿ãã¤ã¤ãããã¾ã¨ããªãã¶ã¤ã³ãç®æãã試ä½åã ã
Eaterã¢ãããæ¶è²»è ãFeederãEaterãå¤æããã¢ããã¨ãã¦å®è£ ããã¦ããã
newtype Feeder s n m a = Feeder { unFeeder :: forall x r. Eater s n x -> (a -> Eater s n x -> m r) -> m r } killEater :: Monad m => Eater s m a -> m a sinkNull :: Monad m => Eater s m () feed :: Monad m => Feeder s n m a -> Eater s n x -> m (a, Eater s n x) type Rancher a b m = Feeder b m (Eater a m) (>-$) :: Monad m => Rancher a b m x -> Eater b m r -> Eater a m r scan :: Monad m => (b -> a -> b) -> b -> Rancher a b m () scan f b = lift await >>= \case Nothing -> return () Just a -> do let !b' = f b a yieldOn liftP b' scan f b' drainF :: Rancher Int a Identity () -> () drainF h = runIdentity $ killEater $ snd $ runIdentity $ feed sourceF $ h >-$ sinkNull sourceF :: Feeder Int Identity Identity () sourceF = yieldMany [1..value]
ãã¡ããã¹ããªã¼ã ã®çµç«¯ã¨æ®ä½ãæ±ããããconduitã«ã¹ãã¼ãã«è² ãã¦ãã¦ã¯ä»æ¹ããªãã
predators
- å©ç¹ iterateeãconduitã¨åçã®å®ç¨çãªè¡¨ç¾åããä¸é¢¨å¤ãã£ãã·ã³ãã«ãªå®è£ ã§å®ç¾
- æ¬ ç¹ çç£è ã使ããã£ã¦æ¶è²»è ãæ®ãã¤ã³ã¯ãªã¡ã³ã¿ã«ãªä½¿ãæ¹ã¯ã§ããªã
Predatorã¯Feederã¨ã¯éã«ãçç£è ãæé£ããæ§é ã¨ãã¦æ¶è²»è ãå®è£ ããã
prey :: Monad m => Predator s n m a -> Prey s n x -> m (Maybe (a, Prey s n x)) type Heterotroph a b m = Predator a m (Prey b m) (@->) :: Monad m => Prey a m x -> Heterotroph a b m r -> Prey b m (Maybe r) scan :: Monad m => (b -> a -> b) -> b -> Heterotroph a b m () scan f b = do a <- awaitOn lift let !b' = f b a lift $ yield b' scan f b' drainPd :: Heterotroph Int a Identity () -> () drainPd h = maybe () fst $ runIdentity $ prey Pd.sinkNull $ sourcePd @-> h sourcePd :: Prey Int Identity () sourcePd = yieldMany [1..value]
conduitã¨å ¨ãç°ãªãã¢ããã¼ãã§ãããªãããæ®ä½ã¨çµç«¯ãå¦çã§ããåçã®é度ãåºã¦ããã®ã§ããã³ã·ã£ã«ãç§ãã¦ãããç§ã®ããæ°ãç¶ãã°ãããªãé²å±ããããããããªãããã¼ãã³ã°ãæ°ã«å ¥ã£ã¦ããã
time 300.7 μs (299.2 μs .. 302.1 μs) 0.999 R² (0.998 R² .. 0.999 R²) mean 314.9 μs (310.0 μs .. 321.2 μs) std dev 19.03 μs (13.81 μs .. 23.15 μs) variance introduced by outliers: 56% (severely inflated)
boombox
- å©ç¹ é«ãæè»æ§ã¨é«ããã©ã¼ãã³ã¹ã両ç«ãã¦ãã
- æ¬ ç¹ APIãé常ã«ä¹ãã
ã¹ããªã¼ã å¦çã®å¤§çµ±ä¸ãç®æãã¦ä½ã£ãã©ã¤ãã©ãªboomboxã¯pipesãããéãã
time 160.7 μs (160.1 μs .. 161.5 μs) 1.000 R² (0.999 R² .. 1.000 R²) mean 163.1 μs (162.1 μs .. 164.1 μs) std dev 3.382 μs (2.810 μs .. 4.125 μs) variance introduced by outliers: 14% (moderately inflated)
çç£ã¨æ¶è²»ã«Tapeã¨PlayerTã¨ããå°ç¨ã®æ§é ãç¨æããå¤æå¨ã¯ä¸¡è
ãçµã¿åããã¦è¡¨ç¾ãããã¹ããªã¼ã ã®æ®ä½å¦çãã·ã¼ã¯ãªã©ãªãã§ã表ç¾ã§ããããå¯è½æ§ãæ®ãããããã¨ãä»ã¨ãªãAPIãä¹ãããRecorder Identity Identity
ãPipe
ã«ç¸å½ããå¤æå¨ã§ãIdentity
ãStore
ã«å·®ãæ¿ããã°ã·ã¼ã¯å¯è½ã«ãªããå½ç¶é常ã®ã¹ããªã¼ã ããã·ã¼ã¯å¯è½ãªã¹ããªã¼ã ã¸ã®å¤æå¨ãå®ç¾©ã§ãããNonEmpty
ã³ã¢ããã使ãã°è¤æ°ã®ä¸çç·ã«åå²ãããããªã¹ããªã¼ã ã表ç¾ã§ãããmachinesã¨éããã©ãã«ã¹ã¿ãã¤ãºãã¦ãå¿
ãåæãã§ããã®ããã¤ã³ãã ã
scanB :: (b -> a -> b) -> b -> Recorder Identity Identity m a b scanB f = go where go b = Tape $ await >>= \x -> let !b' = f b x in return (b', pure $ go b') sourceB :: Tape Identity Maybe Int sourceB = tap [1..value] drainB :: Recorder Identity Identity Maybe Int a -> () drainB h = maybe () (\(_,_,r) -> r) $ sourceB @.$ h >-$ forever await
ããã©ã¼ãã³ã¹ã¯è¯å¥½ãªã®ã§ãå ¨ã©ã¤ãã©ãªã®çµ±ä¸ãç®æãã¦ç 究ãç¶ãã¦ããããã
machinecell
- å©ç¹ ä»ã®ã¨ããå¯ä¸ã®Arrowãã¼ã¹ã®ã©ã¤ãã©ãª
- æ¬ ç¹ é ã
time 185.5 ms (184.1 ms .. 188.5 ms) 1.000 R² (1.000 R² .. 1.000 R²) mean 184.2 ms (183.4 ms .. 185.3 ms) std dev 1.176 ms (562.9 μs .. 1.702 ms) variance introduced by outliers: 14% (moderately inflated)
machinecellã¯ã¢ãã¼å¤æåã¨ãã¦ã¹ããªã¼ã å¤æå¨ãå®è£ ããç°è²ã®ããã±ã¼ã¸ã ãããããªããªãéããã¨æãããåä½ããã¤ã¯ãã§ã¯ãªãããªã§ãpipesã®1000åã®1ã®éãã ã£ããä»å¾ã®æ¹è¯ã«æå¾ ãããã
drainMc :: Mc.ProcessA (->) (Mc.Event Int) a -> () drainMc h = Mc.run_ (h >>> arr (const Mc.noEvent)) [1..value] , bench "machinecell" $ whnf drainMc (Mc.evMap (+) >>> Mc.accum 0)
ã¾ã¨ã
ãã¾ãåã£ããã¨ãããªããªãã°ãä»ã®ã¨ããpipesãç¡é£ã ã¨èãã¦ãããããããçµç«¯å¦çãã·ã¼ã¯ãªã©ã絡ãã¨ãã©ã®ã©ã¤ãã©ãªãå°é£ã«ç´é¢ããã決çã¯æªã ã¤ãã¦ããªãã