@@ -10,6 +10,7 @@ import Criterion
1010import Data.Csv
1111import Data.Proxy
1212import Data.Typeable
13+ import Generic.Either
1314import Generic.U2
1415import Generic.U4
1516import Generic.U8
@@ -19,94 +20,176 @@ import Generic.U32
1920
2021genericFieldBench :: Benchmark
2122genericFieldBench = bgroup " genericField"
22- [ bgroup " parseField: ok"
23- [ mkParseSuccessBench (Proxy @ U2 ) (Proxy @ U2Generic ) (Proxy @ U2GenericStripPrefix )
24- , mkParseSuccessBench (Proxy @ U4 ) (Proxy @ U4Generic ) (Proxy @ U4GenericStripPrefix )
25- , mkParseSuccessBench (Proxy @ U8 ) (Proxy @ U8Generic ) (Proxy @ U8GenericStripPrefix )
26- , mkParseSuccessBench (Proxy @ U16 ) (Proxy @ U16Generic ) (Proxy @ U16GenericStripPrefix )
27- , mkParseSuccessBench (Proxy @ U32 ) (Proxy @ U32Generic ) (Proxy @ U32GenericStripPrefix )
23+ [ bgroup " parseField:ok"
24+ [ mkParseSuccessBench (genRange @ U2 )
25+ , mkParseSuccessBench (genRange @ U2Generic )
26+ , mkParseSuccessBench (genRange @ U2GenericStripPrefix )
27+ , mkParseSuccessBench (genRange @ U4 )
28+ , mkParseSuccessBench (genRange @ U4Generic )
29+ , mkParseSuccessBench (genRange @ U4GenericStripPrefix )
30+ , mkParseSuccessBench (genRange @ U8 )
31+ , mkParseSuccessBench (genRange @ U8Generic )
32+ , mkParseSuccessBench (genRange @ U8GenericStripPrefix )
33+ , mkParseSuccessBench (genRange @ U16 )
34+ , mkParseSuccessBench (genRange @ U16Generic )
35+ , mkParseSuccessBench (genRange @ U16GenericStripPrefix )
36+ , mkParseSuccessBench (genRange @ U32 )
37+ , mkParseSuccessBench (genRange @ U32Generic )
38+ , mkParseSuccessBench (genRange @ U32GenericStripPrefix )
39+ , mkParseSuccessBench $ take 32 $ cycle @ ManualEither0
40+ [ LManual 1
41+ , RManual ' !'
42+ ]
43+ , mkParseSuccessBench $ take 32 $ cycle @ GenericEither0
44+ [ LGeneric 1
45+ , RGeneric ' !'
46+ ]
47+ , mkParseSuccessBench $ take 32 $ cycle @ ManualEither1
48+ [ LManual $ LManual 1
49+ , LManual $ RManual ' !'
50+ , RManual $ LManual 1
51+ , RManual $ RManual ' !'
52+ ]
53+ , mkParseSuccessBench $ take 32 $ cycle @ GenericEither1
54+ [ LGeneric $ LGeneric 1
55+ , LGeneric $ RGeneric ' !'
56+ , RGeneric $ LGeneric 1
57+ , RGeneric $ RGeneric ' !'
58+ ]
59+ , mkParseSuccessBench $ take 32 $ cycle @ ManualEither2
60+ [ LManual $ LManual $ LManual 1
61+ , LManual $ LManual $ RManual ' !'
62+ , LManual $ RManual $ LManual 1
63+ , LManual $ RManual $ RManual ' !'
64+ , RManual $ LManual $ LManual 1
65+ , RManual $ LManual $ RManual ' !'
66+ , RManual $ RManual $ LManual 1
67+ , RManual $ RManual $ RManual ' !'
68+ ]
69+ , mkParseSuccessBench $ take 32 $ cycle @ GenericEither2
70+ [ LGeneric $ LGeneric $ LGeneric 1
71+ , LGeneric $ LGeneric $ RGeneric ' !'
72+ , LGeneric $ RGeneric $ LGeneric 1
73+ , LGeneric $ RGeneric $ RGeneric ' !'
74+ , RGeneric $ LGeneric $ LGeneric 1
75+ , RGeneric $ LGeneric $ RGeneric ' !'
76+ , RGeneric $ RGeneric $ LGeneric 1
77+ , RGeneric $ RGeneric $ RGeneric ' !'
78+ ]
79+ , mkParseSuccessBench $ take 32 $ cycle @ ManualEither3
80+ [ LManual $ LManual $ LManual $ LManual 1
81+ , LManual $ LManual $ LManual $ RManual ' !'
82+ , LManual $ LManual $ RManual $ LManual 1
83+ , LManual $ LManual $ RManual $ RManual ' !'
84+ , LManual $ RManual $ LManual $ LManual 1
85+ , LManual $ RManual $ LManual $ RManual ' !'
86+ , LManual $ RManual $ RManual $ LManual 1
87+ , LManual $ RManual $ RManual $ RManual ' !'
88+ , RManual $ LManual $ LManual $ LManual 1
89+ , RManual $ LManual $ LManual $ RManual ' !'
90+ , RManual $ LManual $ RManual $ LManual 1
91+ , RManual $ LManual $ RManual $ RManual ' !'
92+ , RManual $ RManual $ LManual $ LManual 1
93+ , RManual $ RManual $ LManual $ RManual ' !'
94+ , RManual $ RManual $ RManual $ LManual 1
95+ , RManual $ RManual $ RManual $ RManual ' !'
96+ ]
97+ , mkParseSuccessBench $ take 32 $ cycle @ GenericEither3
98+ [ LGeneric $ LGeneric $ LGeneric $ LGeneric 1
99+ , LGeneric $ LGeneric $ LGeneric $ RGeneric ' !'
100+ , LGeneric $ LGeneric $ RGeneric $ LGeneric 1
101+ , LGeneric $ LGeneric $ RGeneric $ RGeneric ' !'
102+ , LGeneric $ RGeneric $ LGeneric $ LGeneric 1
103+ , LGeneric $ RGeneric $ LGeneric $ RGeneric ' !'
104+ , LGeneric $ RGeneric $ RGeneric $ LGeneric 1
105+ , LGeneric $ RGeneric $ RGeneric $ RGeneric ' !'
106+ , RGeneric $ LGeneric $ LGeneric $ LGeneric 1
107+ , RGeneric $ LGeneric $ LGeneric $ RGeneric ' !'
108+ , RGeneric $ LGeneric $ RGeneric $ LGeneric 1
109+ , RGeneric $ LGeneric $ RGeneric $ RGeneric ' !'
110+ , RGeneric $ RGeneric $ LGeneric $ LGeneric 1
111+ , RGeneric $ RGeneric $ LGeneric $ RGeneric ' !'
112+ , RGeneric $ RGeneric $ RGeneric $ LGeneric 1
113+ , RGeneric $ RGeneric $ RGeneric $ RGeneric ' !'
114+ ]
28115 ]
29- , bgroup " parseField: fail"
30- [ mkParseFailBench (Proxy @ U2 ) (Proxy @ U2Generic ) (Proxy @ U2GenericStripPrefix )
31- , mkParseFailBench (Proxy @ U4 ) (Proxy @ U4Generic ) (Proxy @ U4GenericStripPrefix )
32- , mkParseFailBench (Proxy @ U8 ) (Proxy @ U8Generic ) (Proxy @ U8GenericStripPrefix )
33- , mkParseFailBench (Proxy @ U16 ) (Proxy @ U16Generic ) (Proxy @ U16GenericStripPrefix )
34- , mkParseFailBench (Proxy @ U32 ) (Proxy @ U32Generic ) (Proxy @ U32GenericStripPrefix )
116+ , bgroup " parseField:fail"
117+ [ mkParseFailBench (Proxy @ U2 )
118+ , mkParseFailBench (Proxy @ U2Generic )
119+ , mkParseFailBench (Proxy @ U2GenericStripPrefix )
120+ , mkParseFailBench (Proxy @ U4 )
121+ , mkParseFailBench (Proxy @ U4Generic )
122+ , mkParseFailBench (Proxy @ U4GenericStripPrefix )
123+ , mkParseFailBench (Proxy @ U8 )
124+ , mkParseFailBench (Proxy @ U8Generic )
125+ , mkParseFailBench (Proxy @ U8GenericStripPrefix )
126+ , mkParseFailBench (Proxy @ U16 )
127+ , mkParseFailBench (Proxy @ U16Generic )
128+ , mkParseFailBench (Proxy @ U16GenericStripPrefix )
129+ , mkParseFailBench (Proxy @ U32 )
130+ , mkParseFailBench (Proxy @ U32Generic )
131+ , mkParseFailBench (Proxy @ U32GenericStripPrefix )
132+ , mkParseFailBench (Proxy @ ManualEither0 )
133+ , mkParseFailBench (Proxy @ ManualEither1 )
134+ , mkParseFailBench (Proxy @ ManualEither2 )
135+ , mkParseFailBench (Proxy @ ManualEither3 )
35136 ]
36137 , bgroup " toField"
37- [ mkToFieldBench (Proxy @ U2 ) (Proxy @ U2Generic ) (Proxy @ U2GenericStripPrefix )
38- , mkToFieldBench (Proxy @ U4 ) (Proxy @ U4Generic ) (Proxy @ U4GenericStripPrefix )
39- , mkToFieldBench (Proxy @ U8 ) (Proxy @ U8Generic ) (Proxy @ U8GenericStripPrefix )
40- , mkToFieldBench (Proxy @ U16 ) (Proxy @ U16Generic ) (Proxy @ U16GenericStripPrefix )
41- , mkToFieldBench (Proxy @ U32 ) (Proxy @ U32Generic ) (Proxy @ U32GenericStripPrefix )
138+ [ mkToFieldBench (genRange @ U2 )
139+ , mkToFieldBench (genRange @ U2Generic )
140+ , mkToFieldBench (genRange @ U2GenericStripPrefix )
141+ , mkToFieldBench (genRange @ U4 )
142+ , mkToFieldBench (genRange @ U4Generic )
143+ , mkToFieldBench (genRange @ U4GenericStripPrefix )
144+ , mkToFieldBench (genRange @ U8 )
145+ , mkToFieldBench (genRange @ U8Generic )
146+ , mkToFieldBench (genRange @ U8GenericStripPrefix )
147+ , mkToFieldBench (genRange @ U16 )
148+ , mkToFieldBench (genRange @ U16Generic )
149+ , mkToFieldBench (genRange @ U16GenericStripPrefix )
150+ , mkToFieldBench (genRange @ U32 )
151+ , mkToFieldBench (genRange @ U32Generic )
152+ , mkToFieldBench (genRange @ U32GenericStripPrefix )
42153 ]
43154 ]
44155
45- type IsBench a = (Bounded a , Enum a , FromField a , ToField a , NFData a )
156+ type IsBench a = (FromField a , ToField a , NFData a , Typeable a )
46157
47- mkParseSuccessBench
48- :: (IsBench a , Typeable a , IsBench generic , IsBench genericWithPrefix )
49- => Proxy a
50- -> Proxy generic
51- -> Proxy genericWithPrefix
52- -> Benchmark
53- mkParseSuccessBench px pxGen pxGenPfx = bgroup (show $ typeRep px)
54- [ mkB " manual" px
55- , mkB " generic" pxGen
56- , mkB " generic with prefix" pxGenPfx
57- ]
58- where
59- {-
60- NB: this all is about sum representations.
61- Manual instance tries to parse constructors from left to right,
62- so parsing the string matching the first constructor is the best case,
63- while parsing the last matcher is the worst case.
64- Generic representation is, however, not that flat (one can check that by
65- exploring 'Rep' of U32) and is more like a balanced binary tree with root
66- being somewhere around U32_16 constructor (rough estimation).
67- To level this discrepency and compare parsing efficiency more accurately
68- we parse the whole range @[minBound..maxBound]@ of possible values for a type.
69- This corresponds to the situation where data values are uniformly distributed.
70- -}
71- mkB
72- :: (Bounded a , Enum a , FromField a , ToField a , NFData a )
73- => String -> Proxy a -> Benchmark
74- mkB name p = env (pure $ map toField $ genEnum p) $ bench name . nf (go p)
75- go :: (FromField a ) => Proxy a -> [Field ] -> [a ]
76- go p = map $ ((\ (Right x) -> x `asProxyTypeOf` p) . parse)
77-
78- mkParseFailBench
79- :: (IsBench a , Typeable a , IsBench generic , IsBench genericWithPrefix )
80- => Proxy a
81- -> Proxy generic
82- -> Proxy genericWithPrefix
83- -> Benchmark
84- mkParseFailBench px pxg pxgp = bgroup (show $ typeRep px)
85- [ bench " manual" $ whnf (\ s -> parse s `asProxyEither` px) mempty
86- , bench " generic" $ whnf (\ s -> parse s `asProxyEither` pxg) mempty
87- , bench " generic with prefix" $ whnf (\ s -> parse s `asProxyEither` pxgp) mempty
88- ]
89-
90- asProxyEither :: Either String a -> Proxy a -> Either String a
91- asProxyEither = const
158+ {-
159+ Manual instance tries to parse constructors from left to right,
160+ so parsing the string matching the first constructor is the best case,
161+ while parsing the last matcher is the worst case.
162+ Generic representation is, however, not that flat (one can check that by
163+ exploring 'Rep' of U32) and is more like a balanced binary tree with root
164+ being somewhere around U32_16 constructor (rough estimation).
165+ To level this discrepency and compare parsing efficiency more accurately
166+ we parse some range (@[minBound..maxBound]@ for enum) of possible values for a type.
167+ This corresponds to the situation where data values are uniformly distributed.
168+ -}
169+ mkParseSuccessBench :: (IsBench a ) => [a ] -> Benchmark
170+ mkParseSuccessBench xs = env (pure $ map toField xs) $
171+ bench (show $ typeRep xs) . nf (map $ (\ (Right x) -> x `asProxyTypeOf` xs) . parse)
92172
93- mkToFieldBench
94- :: (IsBench a , Typeable a , IsBench generic , IsBench genericWithPrefix )
95- => Proxy a
96- -> Proxy generic
97- -> Proxy genericWithPrefix
98- -> Benchmark
99- mkToFieldBench px pxg pxgp = bgroup (show $ typeRep px)
100- [ mkB " manual" px
101- , mkB " generic" pxg
102- , mkB " generic with prefix" pxgp
103- ]
173+ mkParseFailBench :: (IsBench a ) => Proxy a -> Benchmark
174+ mkParseFailBench px = bench (show $ typeRep px) $
175+ nf (\ s -> parse s `asProxyEither` px) mempty
104176 where
105- mkB :: (Bounded a , Enum a , ToField a ) => String -> Proxy a -> Benchmark
106- mkB name = bench name . nf (map toField) . genEnum
177+ asProxyEither :: Either String a -> Proxy a -> Either String a
178+ asProxyEither x _ = x
179+
180+ mkToFieldBench :: (IsBench a ) => [a ] -> Benchmark
181+ mkToFieldBench xs = env (pure xs) $ bench (show $ typeRep xs) . nf (map toField)
107182
108183parse :: (FromField a ) => Field -> Either String a
109184parse = runParser . parseField
110185
111- genEnum :: (Bounded a , Enum a ) => Proxy a -> [a ]
112- genEnum _ = [minBound .. maxBound ]
186+ genRange :: (Bounded a , Enum a ) => [a ]
187+ genRange = take 32 $ cycle [minBound .. maxBound ]
188+
189+ -- manualEither0 :: (Int -> a) -> (Char -> a) -> [a]
190+ -- either0 f g = [f 1, g '!']
191+
192+ -- either1 :: (a -> b) -> (a -> b) -> [a]
193+ -- either1 f g = do
194+ -- x <- either0 f g
195+ -- [f x, g x]
0 commit comments