Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Comparison with hermes-json benchmarks #1001

Merged
merged 2 commits into from
Feb 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
11 changes: 1 addition & 10 deletions .github/workflows/haskell-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@ jobs:
if [ $((HCNUMVER >= 80200)) -ne 0 ] ; then echo "package aeson-benchmarks" >> cabal.project ; fi
if [ $((HCNUMVER >= 80200)) -ne 0 ] ; then echo " ghc-options: -Werror=missing-methods" >> cabal.project ; fi
cat >> cabal.project <<EOF
allow-newer: hermes-json:attoparsec-iso8601
EOF
$HCPKG list --simple-output --names-only | perl -ne 'for (split /\s+/) { print "constraints: $_ installed\n" unless /^(aeson|aeson-benchmarks|aeson-examples|attoparsec-iso8601)$/; }' >> cabal.project.local
cat cabal.project
Expand All @@ -260,10 +261,6 @@ jobs:
key: ${{ runner.os }}-${{ matrix.compiler }}-${{ github.sha }}
path: ~/.cabal/store
restore-keys: ${{ runner.os }}-${{ matrix.compiler }}-
- name: install dependencies
run: |
$CABAL v2-build $ARG_COMPILER --disable-tests --disable-benchmarks --dependencies-only -j2 all
$CABAL v2-build $ARG_COMPILER $ARG_TESTS $ARG_BENCH --dependencies-only -j2 all
- name: build w/o tests
run: |
$CABAL v2-build $ARG_COMPILER --disable-tests --disable-benchmarks all
Expand All @@ -289,31 +286,25 @@ jobs:
rm -f cabal.project.local
- name: constraint set text-2.0
run: |
$CABAL v2-build $ARG_COMPILER --enable-tests --disable-benchmarks --constraint='text ^>=2.0' --dependencies-only -j2 all
$CABAL v2-build $ARG_COMPILER --enable-tests --disable-benchmarks --constraint='text ^>=2.0' all
$CABAL v2-test $ARG_COMPILER --enable-tests --disable-benchmarks --constraint='text ^>=2.0' all
- name: constraint set text-1.2
run: |
if [ $((HCNUMVER < 90400)) -ne 0 ] ; then $CABAL v2-build $ARG_COMPILER --enable-tests --disable-benchmarks --constraint='text ^>=1.2.3.0' --dependencies-only -j2 all ; fi
if [ $((HCNUMVER < 90400)) -ne 0 ] ; then $CABAL v2-build $ARG_COMPILER --enable-tests --disable-benchmarks --constraint='text ^>=1.2.3.0' all ; fi
if [ $((HCNUMVER < 90400)) -ne 0 ] ; then $CABAL v2-test $ARG_COMPILER --enable-tests --disable-benchmarks --constraint='text ^>=1.2.3.0' all ; fi
- name: constraint set bytestring-0.11.2.0
run: |
$CABAL v2-build $ARG_COMPILER --disable-tests --disable-benchmarks --constraint='bytestring >= 0.11.2.0' --dependencies-only -j2 all
$CABAL v2-build $ARG_COMPILER --disable-tests --disable-benchmarks --constraint='bytestring >= 0.11.2.0' all
- name: constraint set ordered-keymap-off
run: |
$CABAL v2-build $ARG_COMPILER --enable-tests --disable-benchmarks --constraint='aeson -ordered-keymap' --dependencies-only -j2 all
$CABAL v2-build $ARG_COMPILER --enable-tests --disable-benchmarks --constraint='aeson -ordered-keymap' all
$CABAL v2-test $ARG_COMPILER --enable-tests --disable-benchmarks --constraint='aeson -ordered-keymap' all
- name: constraint set ordered-keymap-on
run: |
$CABAL v2-build $ARG_COMPILER --enable-tests --disable-benchmarks --constraint='aeson +ordered-keymap' --dependencies-only -j2 all
$CABAL v2-build $ARG_COMPILER --enable-tests --disable-benchmarks --constraint='aeson +ordered-keymap' all
$CABAL v2-test $ARG_COMPILER --enable-tests --disable-benchmarks --constraint='aeson +ordered-keymap' all
- name: constraint set cffi
run: |
if [ $((HCNUMVER < 90400)) -ne 0 ] ; then $CABAL v2-build $ARG_COMPILER --enable-tests --disable-benchmarks --constraint='aeson +cffi' --dependencies-only -j2 all ; fi
if [ $((HCNUMVER < 90400)) -ne 0 ] ; then $CABAL v2-build $ARG_COMPILER --enable-tests --disable-benchmarks --constraint='aeson +cffi' all ; fi
if [ $((HCNUMVER < 90400)) -ne 0 ] ; then $CABAL v2-test $ARG_COMPILER --enable-tests --disable-benchmarks --constraint='aeson +cffi' all ; fi
- name: save cache
Expand Down
7 changes: 7 additions & 0 deletions benchmarks/aeson-benchmarks.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,13 @@ executable aeson-benchmark-suite
other-modules: Compare.JsonBuilder
build-depends: json-builder

if impl(ghc >=8.10)
build-depends: hermes-json >=0.2.0.1
other-modules:
CompareWithHermes
Data.Hermes.Aeson
Twitter.Hermes

if !flag(text2)
-- buffer-builder might work with text-2 sometime
build-depends: buffer-builder
Expand Down
80 changes: 80 additions & 0 deletions benchmarks/bench/CompareWithHermes.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE OverloadedStrings #-}

module CompareWithHermes (benchmark) where

import Prelude.Compat
import Bench

import Data.Maybe (fromMaybe)
import qualified Data.Aeson as A
import qualified Data.Aeson.Decoding as A.D
import qualified Data.Aeson.Parser.Internal as I
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as BL
import qualified Data.Hermes as H
import qualified Data.Hermes.Aeson as H.A
import qualified Twitter as T
import Twitter.Manual () -- fair comparison with manual Hermes decoders
import Twitter.Hermes

import Utils

decode :: BL.ByteString -> T.Result
decode s = fromMaybe (error "fail to parse via Aeson") $ A.decode s

decode' :: BL.ByteString -> T.Result
decode' s = fromMaybe (error "fail to parse via Aeson") $ A.decode' s

decodeS :: BS.ByteString -> T.Result
decodeS s = fromMaybe (error "fail to parse via Aeson") $ A.decodeStrict' s

decodeTS :: BS.ByteString -> T.Result
decodeTS s = fromMaybe (error "fail to parse via Aeson") $ A.D.decodeStrict s

decodeIP :: BL.ByteString -> T.Result
decodeIP s = fromMaybe (error "fail to parse via Parser.decodeWith") $
I.decodeWith I.jsonEOF A.fromJSON s

decodeH :: BS.ByteString -> T.Result
decodeH s = case H.decodeEither twitterResultDecoder s of
Right result -> result
Left err -> error (show err)

-- decode to A.Value, and then use A.FromJSON instance.
decodeHA :: BS.ByteString -> T.Result
decodeHA s = case H.decodeEither H.A.valueDecoder s of
Left err -> error (show err)
Right v -> case A.fromJSON v of
A.Success x -> x
A.Error err -> error (show err)

benchmark :: Benchmark
benchmark =
env (readL enFile) $ \enA ->
env (readS enFile) $ \enS ->
env (readL jpFile) $ \jpA ->
env (readS jpFile) $ \jpS ->
bgroup "compare-hermes" [
bgroup "decode" [
bgroup "en" [
bench "aeson/lazy" $ nf decode enA
, bench "aeson/strict" $ nf decode' enA
, bench "aeson/stricter" $ nf decodeS enS
, bench "aeson/parser" $ nf decodeIP enA
, bench "aeson/tokens/strict" $ nf decodeTS enS
, bench "hermes" $ nf decodeH enS
, bench "hermes/aeson" $ nf decodeHA enS
]
, bgroup "jp" [
bench "aeson" $ nf decode jpA
, bench "aeson/stricter" $ nf decodeS jpS
, bench "aeson/tokens/strict" $ nf decodeTS jpS
, bench "hermes" $ nf decodeH jpS
, bench "hermes/aeson" $ nf decodeHA jpS
]
]
]
where
enFile = "twitter100.json"
jpFile = "jp100.json"
18 changes: 18 additions & 0 deletions benchmarks/bench/Data/Hermes/Aeson.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
module Data.Hermes.Aeson where

import Control.Applicative ((<|>))

import qualified Data.Hermes as H
import qualified Data.Aeson as A
import qualified Data.Vector as V
import qualified Data.Aeson.Key as K
import qualified Data.Aeson.KeyMap as KM

valueDecoder :: H.Value -> H.Decoder A.Value
valueDecoder v =
A.String <$> H.text v <|>
A.Number <$> H.scientific v <|>
A.Bool <$> H.bool v <|>
A.Object . KM.fromList <$> H.objectAsKeyValues (pure . K.fromText) valueDecoder v <|>
A.Array . V.fromList <$> H.list valueDecoder v <|>
A.Null <$ H.nullable (\_ -> fail "notnull") v
50 changes: 50 additions & 0 deletions benchmarks/bench/Twitter/Hermes.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE OverloadedStrings #-}
module Twitter.Hermes where

import Prelude.Compat

import Data.Int (Int64)

import qualified Data.Hermes as H
import qualified Twitter as T

twitterResultDecoder :: H.Value -> H.Decoder T.Result
twitterResultDecoder = H.withObject $ \obj ->
T.Result
<$> H.atKey "results" (H.list storyDecoder) obj
<*> H.atKey "max_id" int64 obj
<*> H.atKey "since_id" int64 obj
<*> H.atKey "refresh_url" H.text obj
<*> H.atKey "next_page" H.text obj
<*> H.atKey "results_per_page" H.int obj
<*> H.atKey "page" H.int obj
<*> H.atKey "completed_in" H.double obj
<*> H.atKey "since_id_str" H.text obj
<*> H.atKey "max_id_str" H.text obj
<*> H.atKey "query" H.text obj

storyDecoder :: H.Value -> H.Decoder T.Story
storyDecoder = H.withObject $ \obj ->
T.Story
<$> H.atKey "from_user_id_str" H.text obj
<*> H.atKey "profile_image_url" H.text obj
<*> H.atKey "created_at" H.text obj
<*> H.atKey "from_user" H.text obj
<*> H.atKey "id_str" H.text obj
<*> H.atKey "metadata" metadataDecoder obj
<*> H.atKey "to_user_id" (H.nullable int64) obj
<*> H.atKey "text" H.text obj
<*> H.atKey "id" int64 obj
<*> H.atKey "from_user_id" int64 obj
<*> pure Nothing -- our bench corpus doesn't have any geolocated tweets
<*> H.atKey "iso_language_code" H.text obj
<*> H.atKey "to_user_id_str" (H.nullable H.text) obj
<*> H.atKey "source" H.text obj

int64 :: H.Value -> H.Decoder Int64
int64 = fmap fromIntegral <$> H.int

metadataDecoder :: H.Value -> H.Decoder T.Metadata
metadataDecoder = H.withObject $ \obj ->
T.Metadata <$> H.atKey "result_type" H.text obj
7 changes: 7 additions & 0 deletions benchmarks/bench/aeson-benchmark-suite.hs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ import Utils
import qualified UnescapePureText1 as Text1
#endif

#if __GLASGOW_HASKELL__ >=810
import qualified CompareWithHermes
#endif

-------------------------------------------------------------------------------
-- Decode bench
-------------------------------------------------------------------------------
Expand Down Expand Up @@ -136,3 +140,6 @@ main = do
]
++ Compare.benchmarks -- compares to different libs (encoding)
++ [ CompareWithJSON.benchmark ]
#if __GLASGOW_HASKELL__ >=810
++ [ CompareWithHermes.benchmark ]
#endif
3 changes: 3 additions & 0 deletions cabal.haskell-ci
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ branches: master
cabal-check: False
docspec: True

-- due hermes-json depending on attoparsec-iso8601
install-dependencies: False

-- GADT docs
haddock: >=8.6

Expand Down
2 changes: 2 additions & 0 deletions cabal.project
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,7 @@ packages: benchmarks
tests: true
benchmarks: true

allow-newer: hermes-json:attoparsec-iso8601

-- packages: https://hackage.haskell.org/package/libperf-0.1/candidate/libperf-0.1.tar.gz
-- packages: https://hackage.haskell.org/package/tasty-perfbench-0.1/candidate/tasty-perfbench-0.1.tar.gz