Skip to content

Commit fe68f25

Browse files
committed
Basic types.
These are heavily inspired by Criterion. The main difference is that unlike Criterion, the `Benchmarkable` type is a monad. This makes it possible to reduce the function space for `env` significantly, avoiding exotic functions that return a different set of benchmarks by inspecting the resource value. Exotic functions are not just a theoretical problem in Criterion: the framework passes `undefined` to get the list of all benchmarks. So being strict on the argument in any way makes the whole thing go boom.
0 parents  commit fe68f25

File tree

7 files changed

+123
-0
lines changed

7 files changed

+123
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.stack-work/

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# hyperion

Setup.hs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
import Distribution.Simple
2+
main = defaultMain

hyperion.cabal

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
name: hyperion
2+
version: 0.1.0.0
3+
synopsis: Next generation microbenchmarking.
4+
description: Please see README.md
5+
homepage: https://github.com/tweag/hyperion#readme
6+
author: Tweag I/O
7+
maintainer: m@tweag.io
8+
category: Benchmarking
9+
build-type: Simple
10+
extra-source-files: README.md
11+
cabal-version: >=1.10
12+
13+
library
14+
hs-source-dirs: src
15+
exposed-modules: Hyperion
16+
build-depends:
17+
base >= 4.9 && < 5,
18+
deepseq >= 1.4,
19+
mtl >= 2.2,
20+
vector >= 0.11
21+
default-language: Haskell2010
22+
23+
source-repository head
24+
type: git
25+
location: https://github.com/tweag/hyperion

shell.nix

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{ghc ? abort "ghc argument missing. Use Stack >= 1.2."}:
2+
3+
with (import <nixpkgs> {});
4+
5+
haskell.lib.buildStackProject {
6+
name = "hyperion";
7+
buildInputs =
8+
[ ncurses5 # For Intero
9+
];
10+
inherit ghc;
11+
LANG = "en_US.utf8";
12+
}

src/Hyperion.hs

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
{-# LANGUAGE GADTSyntax #-}
2+
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
3+
{-# LANGUAGE ExistentialQuantification #-}
4+
5+
module Hyperion where
6+
7+
import Control.Applicative
8+
import Control.Exception (evaluate)
9+
import Control.Monad.State (State, modify')
10+
import Data.Monoid ((<>))
11+
import Data.Vector (Vector)
12+
import Data.Int (Int64)
13+
import Control.DeepSeq
14+
15+
newtype Benchmarkable a = Benchmarkable { unBenchmarkable :: State (Int64 -> IO ()) a }
16+
deriving (Functor, Applicative, Monad)
17+
18+
data Resource r = Resource r
19+
20+
use :: Resource r -> Benchmarkable r
21+
use (Resource x) = return x
22+
23+
data Benchmark where
24+
Bench :: String -> Benchmarkable () -> Benchmark
25+
Group :: String -> [Benchmark] -> Benchmark
26+
Shared :: NFData r => IO r -> (Resource r -> Benchmark) -> Benchmark
27+
Index :: Vector a -> Benchmark -> Benchmark
28+
29+
bench :: String -> Benchmarkable () -> Benchmark
30+
bench = Bench
31+
32+
bgroup :: String -> [Benchmark] -> Benchmark
33+
bgroup = Group
34+
35+
env :: NFData r => IO r -> (Resource r -> Benchmark) -> Benchmark
36+
env = Shared
37+
38+
-- | Apply an argument to a function, and evaluate the result to weak
39+
-- head normal form (WHNF).
40+
whnf :: (a -> b) -> a -> Benchmarkable ()
41+
whnf f x = Benchmarkable $ modify' (<> pureFunc id f x)
42+
{-# INLINE whnf #-}
43+
44+
-- | Apply an argument to a function, and evaluate the result to head
45+
-- normal form (NF).
46+
nf :: NFData b => (a -> b) -> a -> Benchmarkable ()
47+
nf f x = Benchmarkable $ modify' (<> pureFunc rnf f x)
48+
{-# INLINE nf #-}
49+
50+
pureFunc :: (b -> c) -> (a -> b) -> a -> Int64 -> IO ()
51+
pureFunc reduce f0 x0 = go f0 x0
52+
where go f x n
53+
| n <= 0 = return ()
54+
| otherwise = evaluate (reduce (f x)) >> go f x (n-1)
55+
{-# INLINE pureFunc #-}
56+
57+
-- | Perform an action, then evaluate its result to head normal form.
58+
-- This is particularly useful for forcing a lazy 'IO' action to be
59+
-- completely performed.
60+
nfIO :: NFData a => IO a -> Benchmarkable ()
61+
nfIO m = Benchmarkable $ modify' (<> impure rnf m)
62+
{-# INLINE nfIO #-}
63+
64+
-- | Perform an action, then evaluate its result to weak head normal
65+
-- form (WHNF). This is useful for forcing an 'IO' action whose result
66+
-- is an expression to be evaluated down to a more useful value.
67+
whnfIO :: IO a -> Benchmarkable ()
68+
whnfIO m = Benchmarkable $ modify' (<> impure id m)
69+
{-# INLINE whnfIO #-}
70+
71+
impure :: (a -> b) -> IO a -> Int64 -> IO ()
72+
impure strategy a = go
73+
where go n
74+
| n <= 0 = return ()
75+
| otherwise = a >>= (evaluate . strategy) >> go (n-1)
76+
{-# INLINE impure #-}

stack.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
resolver: lts-7.14
2+
packages:
3+
- '.'
4+
5+
nix:
6+
shell-file: shell.nix

0 commit comments

Comments
 (0)