diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright Tony Day (c) 2016
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+    * Neither the name of Tony Day nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/examples/examples.hs b/examples/examples.hs
new file mode 100644
--- /dev/null
+++ b/examples/examples.hs
@@ -0,0 +1,250 @@
+{-# LANGUAGE BangPatterns #-}
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# OPTIONS_GHC -Wall #-}
+{-# OPTIONS_GHC -fno-warn-type-defaults #-}
+{-# OPTIONS_GHC -fno-warn-name-shadowing #-}
+{-# OPTIONS_GHC -fno-warn-unused-do-bind #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+{-# LANGUAGE NoImplicitPrelude #-}
+
+module Main where
+
+import Data.Scientific
+import Formatting
+import Options.Generic
+import Perf
+import Perf.Analysis
+import Protolude hiding ((%))
+import qualified Data.Map as Map
+import qualified Data.Text as Text
+import qualified Data.Vector as V
+import qualified Data.Vector.Storable as S
+import qualified Data.Vector.Unboxed as U
+
+data Opts = Opts
+  { runs :: Maybe Int -- <?> "number of runs"
+  , sumTo :: Maybe Int -- <?> "sum to this number"
+  , sumsTo :: Maybe [Int] -- <?> "sum to these numbers"
+  } deriving (Generic, Show)
+
+instance ParseField [Int]
+
+instance ParseRecord Opts
+
+-- | useful as a benchmark versus the `ticks` functions being imported
+ticksInFile :: (NFData b) => Int -> (a -> b) -> a -> IO ([Cycle], b)
+ticksInFile n0 f a = go f a n0 []
+  where
+    go f a n ts
+      | n <= 0 = pure (ts, f a)
+      | otherwise = do
+          (t,_) <- tick f a
+          go f a (n - 1) (t:ts)
+
+-- | interposition of a lambda
+fLambda :: Int -> Int
+fLambda = \x -> foldl' (+) 0 [1 .. x]
+
+-- | monomorphic version
+fMono :: Int -> Int
+fMono x = foldl' (+) 0 [1 .. x]
+
+-- | polymorphic function version
+fPoly :: (Integral b) => b -> b
+fPoly x = foldl' (+) 0 [1 .. x]
+
+main :: IO ()
+main = do
+  o :: Opts <- getRecord "perf examples"
+  let !n = fromMaybe 1000 (runs o)
+  let !a = fromMaybe 1000 (sumTo o)
+  let !as = [1, 10, 100, 1000 :: Int]
+
+  writeFile "other/run.md" $
+    code
+      [ formatInt "number of runs:" 2 n
+      , formatInt "accumulate to:" 2 a
+      , sformat ((right 24 ' ' %. stext)%stext) "function:" "foldl' (+) 0"
+      ]
+
+  -- | tick_ testing
+  prewarmup <- tick_
+  _ <- warmup 100
+  onetick <- tick_
+  ticks' <- replicateM 10 tick_
+  manyticks <- replicateM 1000000 tick_
+  let avticks = average manyticks
+  let qticks = deciles 5 manyticks
+  let tick999 = percentile 0.999 manyticks
+  let tick99999 = percentile 0.99999 manyticks
+  let tick99 = percentile 0.99 manyticks
+  let tick40 = percentile 0.4 manyticks
+  writeFile "other/tick_.md" $
+    code
+      [ "pre warmup: " <> show prewarmup <> " cycles"
+      , "one tick_: " <> show onetick <> " cycles"
+      , "next 10: " <> show ticks'
+      , "average over 1m: " <> sformat (fixed 2) avticks <> " cycles"
+      , "99.999% perc: " <> sformat commas (floor tick99999 :: Integer)
+      , "99.9% perc: " <> sformat (fixed 2) tick999
+      , "99th perc:  " <> sformat (fixed 2) tick99
+      , "40th perc:  " <> sformat (fixed 2) tick40
+      , "[min, 20th, .. 80th, max]:"
+      , mconcat (sformat (" " % prec 4) . fromFloatDigits <$> qticks)
+      ]
+
+  -- | tick tests
+  (t, resultPrime) <- tick fMono a
+  print resultPrime
+  (t2,_) <- tick fMono a
+  (t3,_) <- tick fMono a
+  (t',_) <- tick' fMono a
+  (tio,_) <- tickIO (pure $ fMono a)
+  -- mystery: if tickNoinline is used at all, all tick computations slow down
+  -- by a factor of 2 (or by a function call)
+  -- (tn,_) <- tickNoinline fMono a
+  t10 <- sequence $ replicate 10 (tick fMono a)
+  t10io <- sequence $ replicate 10 (tickIO $ pure (fMono a))
+  -- t10n <- sequence $ replicate 10 (tickNoinline fMono a)
+  t10' <- sequence $ replicate 10 (tick fMono a)
+  writeFile "other/tick.md" $
+    code
+      [ "sum to " <> show a
+      , "first measure: " <> show t <> " cycles"
+      , "second measure: " <> show t2 <> " cycles"
+      , "third measure: " <> show t3 <> " cycles"
+      , "tick': " <> show t' <> " cycles"
+      , "tickIO: " <> show tio <> " cycles"
+      -- , "tickNoinline: " <> show tn <> " cycles"
+      , "tick * 10: " <> show (fst <$> t10)
+      , "tickIO * 10: " <> show (fst <$> t10io)
+      -- , "tickNoinline * 10: " <> show (fst <$> t10n)
+      , "tick * 10: " <> show (fst <$> t10')
+      ]
+
+  -- | ticks and ticksIO
+  (rpure, _) <- ticks n fMono a
+  (rpurePoly, _) <- ticks n fPoly a
+  (rpureLambda, _) <- ticks n fLambda a
+  (rio, _) <- ticksIO n (pure $ fMono a)
+  (rioPoly, _) <- ticksIO n (pure $ fPoly a)
+  (rioLambda, _) <- ticksIO n (pure $ fLambda a)
+
+  writeFile "other/ticks.md" $
+    code [ "acc = " <> show a <> " n = " <> show n
+         , formatRunHeader
+         , formatRun "monomorphic" 2 $ rpure
+         , formatRun "includes lambda" 2 $ rpureLambda
+         , formatRun "polymorphic" 2 $ rpurePoly
+         , formatRun "ticksIO mono" 2 $ rio
+         , formatRun "ticksIO lambda" 2 $ rioLambda
+         , formatRun "ticksIO poly" 2 $ rioPoly
+         ]
+
+  gaps <- sequence $ (\a -> formatGap a <$> tickIO (ticks n fPoly a)) <$> as
+  writeFile "other/ticksCost.md" $ code gaps
+
+  -- | ns testing
+  css <-
+    fmap (fmap fst) <$>
+    sequence ((replicateM n . tick fMono) <$> as)
+  let r12 =
+        "(replicateM n . tick fMono) <$> as: " <>
+        mconcat (sformat (" " %prec 3) <$>
+                 (fromFloatDigits . percentile 0.4 <$> css))
+  (ts, _) <- ns (ticks n fMono) as
+  let r13 =
+        "ns (ticks n fMono) as: " <>
+        mconcat (sformat (" " %prec 3) <$>
+                 (fromFloatDigits . percentile 0.4 <$> ts))
+  writeFile "other/tickns.md" $
+    code ["sum to's " <> show as, r13, r12]
+
+  -- | vectors
+  let asl :: [Int]
+      asl = [1 .. a]
+  let suml :: [Int] -> Int
+      suml = foldl' (+) 0
+  (rlist, _) <- ticks n suml asl
+  let sumv :: V.Vector Int -> Int
+      sumv = V.foldl (+) 0
+  let asv :: V.Vector Int =
+        V.generate a identity
+  (rboxed, _) <- ticks n sumv asv
+  let sums :: S.Vector Int -> Int
+      sums = S.foldl (+) 0
+  let ass :: S.Vector Int =
+        S.generate a identity
+  (rstorable, _) <- ticks n sums ass
+  let sumu :: U.Vector Int -> Int
+      sumu = U.foldl (+) 0
+  let asu :: U.Vector Int =
+        U.generate a identity
+  (runboxed, _) <- ticks n sumu asu
+  writeFile "other/vector.md" $
+    code
+    [ "sum to " <> show a
+    , formatRun "ticks list" 2 $ rlist
+    , formatRun "ticks boxed" 2 $ rboxed
+    , formatRun "ticks storable" 2 $ rstorable
+    , formatRun "ticks unboxed" 2 $ runboxed
+    ]
+
+  -- WHNF
+  (rnf, _) <- tick (fmap fMono) (Just a)
+  (rwhnf, _) <- tick (fmap fMono) (Just a)
+  (rnfs, _) <- ticks n (fmap fMono) (Just a)
+  (rwhnfs, _) <- ticksWHNF n (fmap fMono) (Just a)
+  (rnfio, _) <- tickIO (pure $ fmap fMono (Just a))
+  (rwhnfio, _) <- tickWHNFIO (pure $ fmap fMono (Just a))
+  (rnfsio, _) <- ticksIO n (pure $ fmap fMono (Just a))
+  (rwhnfsio, _) <- ticksWHNFIO n (pure $ fmap fMono (Just a))
+
+  writeFile "other/whnf.md" $ code
+    [ "sum to " <> show a
+    , formatInt "tick" 2 $ rnf
+    , formatInt "tickWHNF" 2 $ rwhnf
+    , formatRun "ticks" 2 rnfs
+    , formatRun "ticksWHNF" 2 rwhnfs
+    , formatInt "tickIO" 2 rnfio
+    , formatInt "tickWHNFIO" 2 rwhnfio
+    , formatRun "ticksIO" 2 rnfsio
+    , formatRun "ticksWHNFIO" 2 rwhnfsio
+    ]
+
+  -- perf basics
+  (result, cs) <- runPerfT $
+      perf "sum" cycles (pure $ foldl' (+) 0 [0 .. a])
+  putStrLn (show (result, cs) :: Text)
+
+  -- PerfT example
+  -- prior to Perfification
+  result <- do
+      txt <- readFile "perf-analysis/examples/examples.hs"
+      let n = Text.length txt
+      let x = foldl' (+) 0 [1..n]
+      putStrLn $ "sum of one to number of characters is: " <>
+          (show x :: Text)
+      pure (n, x)
+
+  -- post-Perfification
+  (result', ms) <- runPerfT $ do
+          txt <- perf "file read" cycles $ readFile "perf-analysis/examples/examples.hs"
+          n <- perf "length" cycles $ pure (Text.length txt)
+          x <- perf "sum" cycles $ pure (foldl' (+) 0 [1..n])
+          perf "print to screen" cycles $
+              putStrLn $ "sum of one to number of characters is: " <>
+              (show x :: Text)
+          pure (n, x)
+
+  when (result == result') $ print "PerfT has no effect on original computation"
+
+  let fmt = sformat ((right 40 ' ' %. stext) %prec 3 % " " % stext)
+  writeFile "other/perf.md" $
+    "\nperf cycle measurements\n---\n" <>
+    code ((\(t,c) -> fmt t (int2Sci c) "cycles") <$> Map.toList ms)
+
diff --git a/perf-analysis.cabal b/perf-analysis.cabal
new file mode 100644
--- /dev/null
+++ b/perf-analysis.cabal
@@ -0,0 +1,68 @@
+-- This file has been generated from package.yaml by hpack version 0.28.2.
+--
+-- see: https://github.com/sol/hpack
+--
+-- hash: 97459822690cecba4e6db836b86eedf2d10cc0a7f8ff32e4c525614241b4af82
+
+name:           perf-analysis
+version:        0.0.1.0
+synopsis:       analysis example using perf
+description:     Analytical tools to use with perf. 
+category:       performance
+homepage:       https://github.com/tonyday567/perf#readme
+bug-reports:    https://github.com/tonyday567/perf/issues
+author:         Tony Day
+maintainer:     tonyday567@gmail.com
+copyright:      Tony Day
+license:        BSD3
+license-file:   LICENSE
+build-type:     Simple
+cabal-version:  >= 1.10
+extra-source-files:
+    stack.yaml
+
+source-repository head
+  type: git
+  location: https://github.com/tonyday567/perf
+  subdir: perf-analysis
+
+library
+  exposed-modules:
+      Perf.Analysis
+  other-modules:
+      Paths_perf_analysis
+  hs-source-dirs:
+      src
+  build-depends:
+      base >=4.7 && <4.12
+    , deepseq
+    , formatting
+    , perf
+    , protolude
+    , scientific
+    , tdigest
+    , text
+    , vector
+  default-language: Haskell2010
+
+executable perf-examples
+  main-is: examples.hs
+  other-modules:
+      Paths_perf_analysis
+  hs-source-dirs:
+      examples
+  ghc-options: -Wall -Wcompat -Wincomplete-record-updates -Wincomplete-uni-patterns -Wredundant-constraints -rtsopts -O2
+  build-depends:
+      base >=4.7 && <4.12
+    , containers
+    , deepseq
+    , formatting
+    , optparse-generic
+    , perf
+    , perf-analysis
+    , protolude
+    , scientific
+    , tdigest
+    , text
+    , vector
+  default-language: Haskell2010
diff --git a/src/Perf/Analysis.hs b/src/Perf/Analysis.hs
new file mode 100644
--- /dev/null
+++ b/src/Perf/Analysis.hs
@@ -0,0 +1,105 @@
+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE DataKinds #-}
+{-# OPTIONS_GHC -Wall #-}
+{-# OPTIONS_GHC -fno-warn-type-defaults #-}
+{-# OPTIONS_GHC -fno-warn-name-shadowing #-}
+{-# OPTIONS_GHC -fno-warn-unused-do-bind #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+{-# LANGUAGE NoImplicitPrelude #-}
+
+-- | analytical functionality for perf
+module Perf.Analysis where
+
+import Data.Scientific
+import Data.TDigest
+import Formatting
+import Perf
+import Protolude hiding ((%))
+import qualified Data.Text as Text
+
+-- | compute deciles
+--
+-- >>> deciles 5 [1..1000]
+-- [1.0,200.5,400.5,600.5,800.5,1000.0]
+--
+-- > c5 <- decile 5 <$> ticks n f a
+--
+deciles :: (Functor f, Foldable f, Integral a) => a -> f a -> [Double]
+deciles n xs =
+  (\x -> fromMaybe 0 $
+    quantile x (tdigest (fromIntegral <$> xs) :: TDigest 25)) <$>
+  ((/ fromIntegral n) . fromIntegral <$> [0 .. n])
+
+-- | compute a percentile
+--
+-- >>> percentile 0.1 [1..1000]
+-- 100.5
+--
+-- > c <- percentile 0.4 . fst <$> ticks n f a
+--
+percentile :: (Functor f, Foldable f, Integral a) => Double -> f a -> Double
+percentile p xs = fromMaybe 0 $ quantile p (tdigest (fromIntegral <$> xs) :: TDigest 25)
+
+-- | fixed precision for a Scientific
+prec :: Int -> Format r (Scientific -> r)
+prec n = scifmt Exponent (Just n)
+
+-- | convert an integral to a Scientific
+--
+-- >>> sformat (left 8 ' ' %. prec 1) (int2Sci 1000)
+-- "   1.0e3"
+--
+int2Sci :: (Integral a) => a -> Scientific
+int2Sci n = scientific (fromIntegral n) 0
+
+-- | format an Integral as a Scientific with a label and precision
+formatInt :: (Integral a) => Text -> Int -> a -> Text
+formatInt label p x =
+        sformat
+          ((right 24 ' ' %. stext) %
+           (left 8 ' ' %. prec p)) label (int2Sci x)
+
+-- | format the first 5 results, and then the 40th percentile
+formatRun :: (Integral a) => Text -> Int -> [a] -> Text
+formatRun label p xs =
+  sformat
+  ((right 24 ' ' %. stext) % stext %
+    (left 9 ' ' %. prec p) %
+    (left 9 ' ' %. prec p))
+  label
+  (Text.intercalate " " $ sformat (left 8 ' ' %. prec p) .
+    int2Sci <$>
+    take 3 xs)
+  (fromFloatDigits $ percentile 0.5 xs)
+  (fromFloatDigits $ average xs)
+
+-- | header for formatRun
+formatRunHeader :: Text
+formatRunHeader =
+  sformat
+  ((right 24 ' ' %. stext) %
+    (left 8 ' ' %. stext) %
+    (left 9 ' ' %. stext) %
+    (left 9 ' ' %. stext) %
+    (left 9 ' ' %. stext) %
+    (left 9 ' ' %. stext))
+  "run"
+  "first"
+  "2nd"
+  "3rd"
+  "median"
+  "av."
+
+-- | format a tick result with an inside and outside measurement
+formatGap :: (Integral a) => Int -> (a, ([a],b)) -> Text
+formatGap a (co, (ci, _)) =
+  sformat
+  ("n = " %(left 7 ' ' %. prec 3)%" outside: "%(left 7 ' ' %. prec 3)%" inside: "%(left 7 ' ' %. prec 3)%" gap: "%(left 7 ' ' %. prec 3))
+  (int2Sci a) (int2Sci co) (int2Sci $ sum ci) (int2Sci $ co - sum ci)
+
+-- | place markdown backtics around text
+code :: [Text] -> Text
+code cs = "\n```\n" <> Text.intercalate "\n" cs <> "\n```\n"
diff --git a/stack.yaml b/stack.yaml
new file mode 100644
--- /dev/null
+++ b/stack.yaml
@@ -0,0 +1,11 @@
+resolver: nightly-2018-04-05
+
+packages:
+  - .
+  - ../perf
+
+extra-deps:
+  - rdtsc-1.3.0.1
+  - tdigest-0.2
+
+allow-newer: true
