mailrank/aeson — pull request #36 by basvandijk (2011-11-02 22:35:22+00:00)
Title: Optimizations

Hi Bryan,

The attached patches make the generic parsing of product types much faster than SYB and as fast as TH! Here are the mean times of all the benchmarks:

```
toJSON
======

D/toJSON/th 4.651263 us
D/toJSON/syb 33.24117 us
D/toJSON/generic 4.747054 us

BigRecord/toJSON/th 9.280455 us
BigRecord/toJSON/syb 49.38863 us
BigRecord/toJSON/generic 9.254596 us

BigProduct/toJSON/th 1.630318 us
BigProduct/toJSON/syb 28.46658 us
BigProduct/toJSON/generic 1.680330 us

BigSum/toJSON/th 52.24108 ns
BigSum/toJSON/syb 1.316442 us
BigSum/toJSON/generic 165.0272 ns

fromJSON
========

D/fromJSON/th 10.04595 us
D/fromJSON/syb 24.23340 us
D/fromJSON/generic 13.21329 us

BigRecord/fromJSON/th 8.754886 us
BigRecord/fromJSON/syb 37.13245 us
BigRecord/fromJSON/generic 30.31769 us !

BigProduct/fromJSON/th 2.376512 us
BigProduct/fromJSON/syb 18.22758 us
BigProduct/fromJSON/generic 2.202151 us

BigSum/fromJSON/th 450.2269 ns
BigSum/fromJSON/syb 4.090644 us
BigSum/fromJSON/generic 13.75523 us !!!
```

The last one: generically parsing big sum types is still a lot slower than SYB. The code is basically this:

```
instance (GFromSum a, GFromSum b) => GFromJSON (a :+: b) where
gParseJSON (Object (M.toList -> [keyVal])) = gParseSum keyVal
gParseJSON v = typeMismatch "sum (:+:)" v
{-# INLINE gParseJSON #-}

class GFromSum f where
gParseSum :: Pair -> Parser (f a)

instance (GFromSum a, GFromSum b) => GFromSum (a :+: b) where
gParseSum keyVal = (L1 <$> gParseSum keyVal) <|> (R1 <$> gParseSum keyVal)
{-# INLINE gParseSum #-}

instance (Constructor c, GFromJSON a, ConsFromJSON a) => GFromSum (C1 c a) where
gParseSum (key, value)
| key == pack (conName (undefined :: t c a p)) = gParseJSON value
| otherwise = notFound $ unpack key
{-# INLINE gParseSum #-}

notFound :: String -> Parser a
notFound key = fail $ "The key \"" ++ key ++ "\" was not found"
{-# INLINE notFound #-}
```

Do you have an idea how to make this faster?

Keep in mind that encoded sum types have a shape like a binary tree as in:

`(a :+: b) :+: (c :+: d)`