1{-# LANGUAGE ConstraintKinds #-}
2{-# LANGUAGE FlexibleContexts #-}
3{-# LANGUAGE FlexibleInstances #-}
5{-# LANGUAGE MultiParamTypeClasses #-}
6{-# LANGUAGE RankNTypes #-}
7{-# LANGUAGE RecursiveDo #-}
8{-# LANGUAGE ScopedTypeVariables #-}
9{-# LANGUAGE TupleSections #-}
10{-# LANGUAGE TypeSynonymInstances #-}
12module Reflex.Bench.Focused where
17import Control.Applicative
19import Control.Monad.Fix
20import Control.Monad.Identity
21import Data.Traversable (for, traverse)
23import qualified Data.Dependent.Map as DMap
25import qualified Data.Map as Map
28import Data.Functor.Misc
39import Prelude hiding (concat, sum)
42mergeTree :: Num a => (Monoid (f [a]), Functor f) => Int -> [f a] -> f a
43mergeTree n es | length es <= n = sum' es
44 | otherwise = mergeTree n subTrees
46 merges = chunksOf n es
47 subTrees = map sum' merges
48 sum' = fmap sum . mconcat . fmap (fmap (:[]) )
50mergeListDyn :: Reflex t => [Dynamic t a] -> Dynamic t [a]
51mergeListDyn = mconcat . fmap (fmap pure)
53mergeTreeDyn :: (Num a, Reflex t) => Int -> [Dynamic t a] -> Dynamic t a
54mergeTreeDyn n dyns | length dyns <= n = sumDyns dyns
55 | otherwise = mergeTreeDyn n $ fmap sumDyns merges
57 merges = chunksOf n dyns
58 sumDyns = fmap sum . mergeListDyn
62-- N events all firing for N frames
63continuous :: TestPlan t m => Word -> Word -> m [Event t Word]
64continuous n frames = for [1..n] $ \i -> plan $ (, i) <$> [1..frames]
66-- Fire N events once every P frames for total N frames (interspersed evenly)
67occasional :: TestPlan t m => Word -> Word -> Word -> m [Event t Word]
68occasional n period frames = traverse plan $ zipWith occs [1..] phases
70 phases = take (fromIntegral n) $ concat (repeat [1..period])
71 occs i phase = (, i) <$> [1, phase+1..frames]
75-- Event which never fires (but isn't 'never')
76never' :: TestPlan t m => m (Event t Word)
80-- Event which fires once on first frame
81event :: TestPlan t m => m (Event t Word)
84-- Event which fires constantly over N frames
85events :: TestPlan t m => Word -> m (Event t Word)
86events n = plan $ (\i -> (i, i)) <$> [1..n]
89-- N events all originating from one event
90fmapFan :: Reflex t => Word -> Event t Word -> [Event t Word]
91fmapFan n e = (\i -> (+i) <$> e) <$> [1..n]
94-- Make a fan using the value in the event converting it to a singleton Map
95-- use a fan of size n (key is the value mod n)
96fanMerge :: Reflex t => Word -> Event t Word -> Event t Word
97fanMerge n e = leftmost $ s <$> [0..n - 1] where
98 s k = select f (Const2 k)
100 toMap k = DMap.singleton (Const2 $ k `mod` n) (Identity k)
102-- Dumb version of the above using simple fmapMaybe to filter
103fmapFanMerge :: Reflex t => Word -> Event t Word -> Event t Word
104fmapFanMerge n e = leftmost $ s <$> [0..n - 1] where
105 s k = flip fmapMaybe e $ \j -> if k == j `mod` n
111-- Create an Event with a Map which is dense to size N
112denseMap :: TestPlan t m => Word -> m (Event t (Map Word Word))
113denseMap n = plan [(1, m)] where
114 m = Map.fromList $ zip [1..n] [1..n]
116iter :: (a -> a) -> Word -> a -> a
118iter f n a = iter f (n - 1) (f a)
120iterM :: Monad m => (a -> m a) -> Word -> a -> m a
121iterM _ 0 a = return a
122iterM f n a = iterM f (n - 1) =<< f a
124fmapChain :: (Functor f) => Word -> f Word -> f Word
125fmapChain = iter (fmap (+1))
127mapDynChain :: (Reflex t, MonadHold t m) => Word -> Dynamic t Word -> m (Dynamic t Word)
128mapDynChain = iterM (return . fmap (+1))
130joinDynChain :: (Reflex t, MonadHold t m) => Word -> Dynamic t Word -> m (Dynamic t Word)
131joinDynChain = iterM (\d -> return $ join $ fmap (const d) d)
133holdDynChain :: (Reflex t, MonadHold t m) => Word -> Dynamic t Word -> m (Dynamic t Word)
134holdDynChain = iterM (\d -> sample (current d) >>= flip holdDyn (updated d))
136buildDynChain :: (Reflex t, MonadHold t m) => Word -> Dynamic t Word -> m (Dynamic t Word)
137buildDynChain = iterM (\d -> do
138 let b = fmap (+1) (current d)
139 e = fmap (*2) (updated d)
140 buildDynamic (sample b) e)
143combineDynChain :: (Reflex t, MonadHold t m) => Word -> Dynamic t Word -> m (Dynamic t Word)
144combineDynChain = iterM (\d -> return $ zipDynWith (+) d d)
146pingPong :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> Event t a -> m (Event t a)
149 d <- foldDyn (const swap) (e1, e2) e
150 let e = switch (fst <$> current d)
153switchFactors :: (Reflex t, MonadHold t m) => Word -> Event t Word -> m (Event t Word)
154switchFactors 0 e = return e
155switchFactors i e = do
156 b <- hold ((+1) <$> e) (e <$ ffilter ((== 0) . (i `mod`)) e)
157 switchFactors (i - 1) (switch b)
160switchChain :: (Reflex t, MonadHold t m, MonadFix m) => Word -> Event t Word -> m (Event t Word)
161switchChain = iterM (\e -> pingPong e ((+1) <$> e))
164switchChain2 :: (Reflex t, MonadHold t m, MonadFix m) => Word -> Event t Word -> m (Event t Word)
165switchChain2 = iterM (\e -> pingPong e ((+1) <$> leftmost [e, e]))
167switchPromptlyChain :: (Reflex t, MonadHold t m) => Word -> Event t Word -> m (Event t Word)
168switchPromptlyChain = iterM $ \e ->
169 switchPromptlyDyn <$> holdDyn e (e <$ e)
172coinChain :: Reflex t => Word -> Event t Word -> Event t Word
173coinChain = iter $ \e -> coincidence (e <$ e)
175fanMergeChain :: Reflex t => Word -> Word -> Event t Word -> Event t Word
176fanMergeChain width = iter $ fanMerge width
178fmapFanMergeChain :: Reflex t => Word -> Word -> Event t Word -> Event t Word
179fmapFanMergeChain width = iter $ fmapFanMerge width
181-- | Data type for a collection (a map) which is updated by providing differences
182data UpdatedMap t k a = UpdatedMap (Map k a) (Event t (Map k (Maybe a)))
184-- This will hopefully become a primitive (faster!)
185switchMergeEvents :: (MonadFix m, MonadHold t m, Reflex t, Ord k) => UpdatedMap t k (Event t a) -> m (Event t (Map k a))
186switchMergeEvents mapChanges = switch . fmap mergeMap <$> holdMap mapChanges
188-- switchMergeMaybe :: (MonadFix m, MonadHold t m, Reflex t, Ord k) => UpdatedMap t k (Event t a) -> m (Event t (Map k a))
189-- switchMergeMaybe (UpdatedMap initial changes) = switchMergeMap initial (fmap (fromMaybe never) <$> changes)
191switchMergeBehaviors :: forall a t m k. (MonadFix m, MonadHold t m, Reflex t, Ord k) => UpdatedMap t k (Behavior t a) -> m (Behavior t (Map k a))
192switchMergeBehaviors mapChanges = pull . joinMap <$> holdMap mapChanges
193 where joinMap m = traverse sample =<< sample m
195-- | Turn an UpdatedMap into a Dynamic by applying the differences to the initial value
196holdMapDyn :: (Reflex t, MonadHold t m, MonadFix m, Ord k) => UpdatedMap t k a -> m (Dynamic t (Map k a))
197holdMapDyn (UpdatedMap initial changes) = foldDyn (flip (Map.foldrWithKey modify)) initial changes
200 modify k Nothing items = Map.delete k items
201 modify k (Just item) items = Map.insert k item items
204-- | Hold an UpdatedMap as a behavior by applying differences to the initial value
205holdMap :: (Reflex t, MonadHold t m, MonadFix m, Ord k) => UpdatedMap t k a -> m (Behavior t (Map k a))
206holdMap = (current <$>) . holdMapDyn
209increasingMerge :: TestPlan t m => [a] -> m (UpdatedMap t Int a)
210increasingMerge xs = do
211 e <- planList (zipWith Map.singleton [1..] (Just <$> xs) ++ [reset])
212 return (UpdatedMap Map.empty e)
214 reset = Map.fromList $ zip [1..] (const Nothing <$> xs)
216decreasingMerge :: (TestPlan t m) => [a] -> m (UpdatedMap t Int a)
217decreasingMerge xs = do
218 e <- planList (zipWith Map.singleton [1..] (const Nothing <$> xs) ++ [reset])
219 return (UpdatedMap initial e)
221 initial = Map.fromList $ zip [1..] xs
222 reset = Just <$> initial
224modifyMerge :: (TestPlan t m) => [a] -> m (UpdatedMap t Int a)
226 e <- planList $ zipWith Map.singleton [1..] (Just <$> xs)
227 return (UpdatedMap initial e)
229 initial = Map.fromList $ zip [1..] xs
232countMany :: (Reflex t, MonadHold t m, MonadFix m) => [Event t a] -> m [Behavior t Int]
233countMany = traverse (fmap current . count)
235countManyDyn :: (Reflex t, MonadHold t m, MonadFix m) => [Event t a] -> m [Dynamic t Int]
236countManyDyn = traverse count
239-- Given a function which creates a sub-network (from a single event)
240-- Create a plan which uses a switch to substitute it
241switches :: TestPlan t m => Word -> (forall n. (MonadHold t n, MonadFix n) => Event t Word -> n (Event t a)) -> m (Event t a)
242switches numFrames f = do
243 es <- events numFrames
244 switch <$> hold never (makeEvents es)
247 makeEvents es = pushAlways (\n -> f (fmap (+n) es)) es
251-- Two sets of benchmarks, one which we're interested in testing subscribe time (as well as time to fire frames)
252-- the other, which we're only interested in time for running frames
253subscribing :: Word -> Word -> [(String, TestCase)]
254subscribing n frames =
255 [ testSub "fmapFanMerge" $ return . mergeList . fmapFan n
256 , testSub "fanMerge" $ return . fanMerge n
257 , testSub "fmapFan/mergeTree" $ return . mergeTree 8 . fmapFan n
258 , testSub "fmapChain" $ return . fmapChain n
259 , testSub "switchChain" $ switchChain n
260 , testSub "switchPromptlyChain" $ switchPromptlyChain n
261 , testSub "switchFactors" $ switchFactors n
262 , testSub "coincidenceChain" $ return . coinChain n
266 testSub :: (Eq a, Show a, NFData a) => String -> (forall t n. (Reflex t, MonadHold t n, MonadFix n) => Event t Word -> n (Event t a)) -> (String, TestCase)
267 testSub name test = testE name (switches frames test)
269-- Set of benchmark to test specifically merging dynamic collections
270-- a pattern which occurs frequently in reflex-dom collections
271merging :: Word -> [(String, TestCase)]
273 [ testE "static events" $ mergeList <$> sparse
274 , testE "increasing events" $ switchMergeEvents =<< increasingMerge =<< sparse
275 , testE "decreasing events" $ switchMergeEvents =<< decreasingMerge =<< sparse
276 , testE "modify events" $ switchMergeEvents =<< modifyMerge =<< sparse
278 , testB "static behaviors" $ pull . traverse sample <$> counters
279 , testB "increasing behaviors" $ switchMergeBehaviors =<< increasingMerge =<< counters
280 , testB "decreasing behaviors" $ switchMergeBehaviors =<< decreasingMerge =<< counters
281 , testB "modify behaviors" $ switchMergeBehaviors =<< modifyMerge =<< counters
284 -- , testE "increasing events/switchMerge" $ switchMergeMaybe =<< increasingMerge =<< sparse
285 -- , testE "decreasing events/switchMerge" $ switchMergeMaybe =<< decreasingMerge =<< sparse
286 -- , testE "modify events/switchMerge" $ switchMergeMaybe =<< modifyMerge =<< sparse
290 sparse :: TestPlan t m => m [Event t Word]
291 sparse = fmap (fmap (+1)) <$> occasional n 8 n
293 counters :: TestPlan t m => m [Behavior t Int]
294 counters = countMany =<< sparse
298fans :: Word -> [(String, TestCase)]
300 [ testE "fanMapChain" $ fanMergeChain n 10 <$> e
301 , testE "fmapFanMapChain" $ fmapFanMergeChain n 10 <$> e
303 , testE "fanMerge " $ fanMerge n <$> events n
305 , testE "fmapFanMerge " $ fmapFanMerge n <$> events n
306 , testE "triggerMerge" $ leftmost <$> for [1..n] (\i -> plan [(i, i)])
311 e :: TestPlan t m => m (Event t Word)
317dynamics :: Word -> [(String, TestCase)]
319 [ testE "mapDynChain" $ fmap updated $ mapDynChain n =<< d
320 , testE "joinDynChain" $ fmap updated $ joinDynChain n =<< d
321 , testE "holdDynChain" $ fmap updated $ holdDynChain n =<< d
322 , testE "buildDynChain" $ fmap updated $ buildDynChain n =<< d
323 , testE "combineDynChain" $ fmap updated $ combineDynChain n =<< d
324 , testE "dense mergeTree" $ fmap (updated . mergeTreeDyn 8) dense
325 , testE "sparse mergeTree" $ fmap (updated . mergeTreeDyn 8) sparse
326 , testE "mergeDyn" $ fmap (updated . fmap sum . mergeListDyn) sparse
329 d :: TestPlan t m => m (Dynamic t Word)
330 d = count =<< events 10
332 sparse, dense :: TestPlan t m => m [Dynamic t Int]
333 sparse = countManyDyn =<< occasional n 8 16
334 dense = countManyDyn =<< continuous n 2
337firing :: Word -> [(String, TestCase)]
339 [ testE "fmapChain" $ fmapChain n <$> e
340 , testE "switchChain2" $ switchChain2 n =<< e
341 , testE "switchPromptlyChain" $ switchPromptlyChain n =<< e
342 , testE "switchFactors" $ switchFactors n =<< e
343 , testE "coincidenceChain" $ coinChain n <$> e
345 , testE "dense mergeTree" $ mergeTree 8 <$> dense
346 , testE "sparse mergeTree" $ mergeTree 8 <$> sparse
348 -- Triggers test failure in Spider
349 --, testE "runFrame" $ events n
351 , testB "sum counters" $ do
353 return $ pull $ sum <$> traverse sample counts
355 , testB "pullChain" $ fmapChain n . current <$> (count =<< events 4)
356 , testB "pullChain2" $ do
360 b <- hold (constant 0) $
361 pushAlways (const $ fmapChain n <$> hold 0 es) e'
364 , testB "counters/pullTree" $ mergeTree 8 <$> counters
368 e :: TestPlan t m => m (Event t Word)
371 dense, sparse :: TestPlan t m => m [Event t Word]
372 dense = continuous n 2
373 sparse = occasional n 8 16
375 counters :: TestPlan t m => m [Behavior t Int]
376 counters = countMany =<< sparse
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list