diff --git a/examples/examples.hs b/examples/examples.hs
--- a/examples/examples.hs
+++ b/examples/examples.hs
@@ -1,17 +1,20 @@
+{-# LANGUAGE BangPatterns #-}
 {-# LANGUAGE DataKinds #-}
 {-# LANGUAGE DeriveGeneric #-}
 {-# LANGUAGE NoImplicitPrelude #-}
 {-# 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 #-}
 
-import qualified Data.List as List
+module Main where
+
 import qualified Data.Text as Text
-import Data.Text.IO (writeFile, readFile)
-import qualified Data.Map as Map
+import Data.Text.IO (writeFile)
 import qualified Data.Vector as V
 import qualified Data.Vector.Storable as S
 import qualified Data.Vector.Unboxed as U
@@ -22,54 +25,92 @@
 
 data Opts = Opts
   { runs :: Maybe Int -- <?> "number of runs"
-  , sumTo :: Maybe Double -- <?> "sum to this number"
+  , sumTo :: Maybe Int -- <?> "sum to this number"
+  , sumsTo :: Maybe [Int] -- <?> "sum to these numbers"
+  , flipMaybe :: Maybe Bool
   } deriving (Generic, Show)
 
+instance ParseField [Int]
+
 instance ParseRecord Opts
 
-ticks :: Int -> (a -> b) -> a -> IO ([Cycle], b)
-ticks n f a = do
-  ts <- replicateM' n (tick f a)
-  pure (fst <$> ts, snd $ List.last ts)
+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)
 
-qtick :: Int -> (a -> b) -> a -> IO (Double, b)
-qtick n f a = do
-  ts <- replicateM' n (tick f a)
-  pure (percentile 0.4 $ fst <$> ts, snd $ List.last ts)
+fLambda :: Int -> Int
+fLambda = \x -> foldl' (+) 0 [1 .. x]
 
+fMono :: Int -> Int
+fMono x = foldl' (+) 0 [1 .. x]
+
+fPoly :: (Enum b, Num b, Additive b) => b -> b
+fPoly x = foldl' (+) 0 [1 .. x]
+
+
+formatone :: (Real a) => Text -> a -> Text
+formatone =
+        sformat
+          ((right 24 ' ' %. stext) %
+           (left 7 ' ' %. prec 3) % " cycles")
+
+runone :: (Real a) => Text -> IO (a, b) -> IO Text
+runone label t = formatone label . fst <$> t
+
 main :: IO ()
 main = do
   o :: Opts <- getRecord "a random bit of text"
   let n = fromMaybe 1000 (runs o)
-  let a = fromMaybe 10000 (sumTo o)
+  let a = fromMaybe 1000 (sumTo o)
+  let !a' = fromIntegral a
+  let !a'' = a'+1
+  let !a''' = a'+1
+  let as = [1, 10, 100, 1000 :: Int]
+  let fm = fromMaybe True (flipMaybe o)
 
-  -- perf
-  -- prior to Perfification
-  result <- do
-      txt <- readFile "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)
+  writeFile "other/run.md" $
+    code
+      [ sformat ((right 24 ' ' %. stext)%prec 3)  "number of runs:" n
+      , sformat ((right 24 ' ' %. stext)%prec 3)  "accumulate to:" a
+      , sformat ((right 24 ' ' %. stext)%stext) "function:" "foldl' (+) 0"
+      ]
 
-  -- post-Perfification
-  (result', ms) <- runPerfT $ do
-          txt <- perf "file read" cycles $ readFile "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)
+  let formatRun :: [Cycle] -> Text -> Text
+      formatRun cs label =
+        sformat
+          ((right 24 ' ' %. stext) % stext %
+           (left 7 ' ' %. prec 3) % " cycles")
+          label
+          (Text.intercalate " " $ sformat (left 7 ' ' %. prec 3) <$>
+           take 5 cs)
+          (percentile 0.4 cs)
 
-  when (result == result') $ print "PerfT preserves computations"
+  let formatRunHeader =
+        sformat
+          ((right 24 ' ' %. stext) %
+           (left 7 ' ' %. stext) %
+           (left 8 ' ' %. stext) %
+           (left 8 ' ' %. stext) %
+           (left 8 ' ' %. stext) %
+           (left 8 ' ' %. stext) %
+           (left 8 ' ' %. stext))
+          "run"
+          "first"
+          "2nd"
+          "3rd"
+          "4th"
+          "5th"
+          "40th %"
 
-  let fmt = sformat ((right 40 ' ' %. stext) %prec 3 % " " % stext)
-  writeFile "other/perf.md" $
-    "\nperf cycle measurements\n---\n" <>
-    code ((\(t,c) -> fmt t c "cycles") <$> Map.toList ms)
+  let run label t = (`formatRun` label) . fst <$> t
 
+  _ <- warmup 100
+
   -- | tick_
   onetick <- tick_
   ticks' <- replicateM 10 tick_
@@ -93,104 +134,138 @@
       , mconcat (sformat (" " % prec 4) <$> qticks)
       ]
 
-
   -- tick
-  _ <- warmup 100
-  let f x = foldl' (+) 0 [1 .. x]
-  (t, _) <- tick f a
-  (ts, _) <- Main.ticks n f a
-  let qt x = (`percentile` x) <$> [0, 0.3, 0.5, 0.9, 0.99, 1]
+  (t, resultPrime) <- tick fMono a'
+  print resultPrime
+  (t2,_) <- tick fMono a'
   writeFile "other/tick.md" $
     code
       [ "sum to " <> show a
       , "first measure: " <> show t <> " cycles"
-      , "average over next " <> show n <> ": " <> sformat (fixed 2) (average ts) <>
-        " cycles"
-      , "[min, 30th, median, 90th, 99th, max]:"
-      , mconcat (sformat (" " % prec 4) <$> qt ts)
+      , "second measure: " <> show t2 <> " cycles"
       ]
 
-  -- | ticks & friends
-  (cs, _) <- Perf.ticks n f a
-  let ft cs t =
-        sformat
-          ((right 40 ' ' %. stext) % prec 3 % " cycles")
-          t
-          (percentile 0.4 cs)
-  let r1 = ft cs "Perf.ticks n f a"
-  (cs, _) <- Main.ticks n f a
-  let r2 = ft cs "Main.ticks n f a"
-  (cs, _) <- Perf.ticksIO n (pure $ f a)
-  let r3 = ft cs "Perf.ticksIO n (pure $ f a)"
-  (c, _) <- Perf.qtick n f a
-  let fq c t = sformat ((right 40 ' ' %. stext) %prec 3 % " cycles") t c
-  let r4 = fq c "Perf.qtick n f a"
-  (c, _) <- Main.qtick n f a
-  let r5 = fq c "Main.qtick n f a"
-  cs <- fmap fst <$> replicateM n (tick f a)
-  let r6 = ft cs "replicateM n (tick f a)"
-  cs <- fmap fst <$> replicateM' n (tick f a)
-  let r7 = ft cs "replicateM' n (tick f a)"
-  cs <- fmap fst <$> replicateM n (tickIO (pure (f a)))
-  let r8 = ft cs "replicateM n (tickIO (pure (f a)))"
-  cs <- fmap fst <$> replicateM n (tick (app (f a)) ())
-  let r9 = ft cs "replicateM n (tick (app (f a)) ())"
-  cs <- fmap fst <$> replicateM n (tick identity (f a))
-  let r10 = ft cs "replicateM n (tick identity (f a))"
-  cs <- fmap fst <$> replicateM n (tick (const (f a)) ())
-  let r11 = ft cs "replicateM n (tick (const (f a)) ())"
+  -- | various versions of tick
+  rpure <- run "ticks" $ ticks n fMono a'
+  rpurePoly <- run "ticks (poly)" $ ticks n fPoly a
+  rpureLambda <- run "ticks (lambda)" $ ticks n fLambda a'
+  rio <- run "ticksIO" $ ticksIO n (pure $ fMono a'')
+  rioPoly <- run "ticksIO (poly)" $ ticksIO n (pure $ fPoly a)
+  rioLambda <- run "ticksIO (lambda)" $ ticksIO n (pure $ fLambda a')
+
+  writeFile "other/ticks.md" $
+    code [ "sum to " <> show a <> " n = " <> show n <> " prime run: " <>
+           sformat (prec 3) t
+         , formatRunHeader
+         , rpure
+         , rpureLambda
+         , rpurePoly
+         , rio
+         , rioLambda
+         , rioPoly
+         ]
+
+  -- | overall ticks cost
+  let formatGap :: Int -> (Cycle, ([Cycle],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))
+          a co (sum ci) (co - sum ci)
+  let runGap t a = formatGap a <$> tickIO t
+  gaps <- sequence $ (\a -> runGap (ticks n fPoly a) a) <$> as
+  writeFile "other/ticksCost.md" $ code gaps
+
+  -- | ns
   css <-
     fmap (fmap fst) <$>
-    sequence ((replicateM n . tick f) <$> [1, 10, 100, 1000, 10000 :: Int])
+    sequence ((replicateM n . tick fMono) <$> as)
   let r12 =
-        "(replicateM n . tick f) <$> [1,10,100,1000,10000]: " <>
+        "(replicateM n . tick fMono) <$> as: " <>
         mconcat (sformat (" " %prec 3) <$> (percentile 0.4 <$> css))
-  (ts, _) <- Perf.tickns n f [1, 10, 100, 1000, 10000 :: Int]
+  (ts, _) <-ns (ticks n fMono) as
   let r13 =
-        "Perf.tickns n f [1,10,100,1000,10000]: " <>
+        "ns (ticks n fMono) as: " <>
         mconcat (sformat (" " %prec 3) <$> (percentile 0.4 <$> ts))
-  writeFile "other/ticks.md" $
-    code ["sum to " <> show a, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13]
-
-  -- vectors
+  writeFile "other/tickns.md" $
+    code ["sum to's " <> show as, r13, r12]
 
-  let sumv :: V.Vector Double -> Double
+  -- | vectors
+  let asl :: [Int]
+      asl = [1 .. a]
+  let suml :: [Int] -> Int
+      suml = foldl' (+) 0
+  rlist <- run "ticks list" $ ticks n suml asl
+  let sumv :: V.Vector Int -> Int
       sumv = V.foldl (+) 0
-
-  let asv :: V.Vector Double =
-        (\x -> V.generate (fromIntegral $ floor x) fromIntegral) a
-
-  (t, _) <- Perf.ticks n sumv asv
-  let rboxed = sformat ("boxed: " %prec 3) (percentile 0.4 t)
-
-  let sums :: S.Vector Double -> Double
+  let asv :: V.Vector Int =
+        V.generate a identity
+  rboxed <- run "ticks boxed" $ ticks n sumv asv
+  let sums :: S.Vector Int -> Int
       sums = S.foldl (+) 0
-
-  let ass :: S.Vector Double =
-        (\x -> S.generate (fromIntegral $ floor x) fromIntegral) a
-
-  (t, _) <- Perf.ticks n sums ass
-  let rstorable = sformat ("storable: " %prec 3) (percentile 0.4 t)
-
-  let sumu :: U.Vector Double -> Double
+  let ass :: S.Vector Int =
+        S.generate a identity
+  rstorable <- run "ticks storable" $ ticks n sums ass
+  let sumu :: U.Vector Int -> Int
       sumu = U.foldl (+) 0
+  let asu :: U.Vector Int =
+        U.generate a identity
+  runboxed <- run "ticks unboxed" $ ticks n sumu asu
+  writeFile "other/vector.md" $
+    code ["sum to " <> show a, rlist, rboxed, rstorable, runboxed]
 
-  let asu :: U.Vector Double =
-        (\x -> U.generate (fromIntegral $ floor x) fromIntegral) a
 
-  (t, _) <- Perf.ticks n sumu asu
-  let runboxed = sformat ("unboxed: " %prec 3) (percentile 0.4 t)
+  -- WHNF
+  let just' x
+        | fm = Just x
+        | otherwise = Nothing
 
-  writeFile "other/vector.md" $
-    code ["sum to " <> show a, rboxed, rstorable, runboxed]
+  rnf <- runone "tick" $ tick (fmap fMono) (just' a')
+  rwhnf <- runone "tickWHNF" $ tick (fmap fMono) (just' a')
+  rnfs <- run "ticks" $ ticks n (fmap fMono) (just' a')
+  rwhnfs <- run "ticksWHNF" $ ticksWHNF n (fmap fMono) (just' a')
 
-  (t, _) <- Perf.ticks n f a
-  putStrLn $ sformat ("Perf.Cycle.ticks n f a: " %prec 3) (percentile 0.4 t)
+  rnfio <- runone "tickIO" $ tickIO (pure $ fmap fMono (just' a'))
+  rwhnfio <- runone "tickWHNFIO" $ tickWHNFIO (pure $ fmap fMono (just' a'))
+  rnfsio <- run "ticksIO" $ ticksIO n (pure $ fmap fMono (just' a'''))
+  rwhnfsio <- run "ticksWHNFIO" $ ticksWHNFIO n (pure $ fmap fMono (just' a'))
 
+  writeFile "other/whnf.md" $
+    code ["sum to " <> show a,
+          rnf, rwhnf, rnfs, rwhnfs, rnfio, rwhnfio, rnfsio, rwhnfsio]
+
+{-
   -- perf basics
   (result, cs) <- runPerfT $
-      perf "sum" cycles (pure $ foldl' (+) 0 [0..10000 :: Integer])
+      perf "sum" cycles (pure $ foldl' (+) 0 [0 .. floor a :: Integer])
   putStrLn (show (result, cs) :: Text)
+
+  -- PerfT example
+  -- prior to Perfification
+  result <- do
+      txt <- readFile "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 "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 preserving computations"
+
+  let fmt = sformat ((right 40 ' ' %. stext) %prec 3 % " " % stext)
+  writeFile "other/perf.md" $
+    "\nperf cycle measurements\n---\n" <>
+    code ((\(t,c) -> fmt t c "cycles") <$> Map.toList ms)
+-}
 
 
 code :: [Text] -> Text
diff --git a/perf.cabal b/perf.cabal
--- a/perf.cabal
+++ b/perf.cabal
@@ -1,5 +1,5 @@
 name: perf
-version: 0.2.0
+version: 0.3.0
 synopsis:
   low-level performance statistics
 description:
@@ -65,7 +65,6 @@
     examples.hs
   build-depends:
     base >= 4.7 && < 4.11,
-    containers,
     formatting,
     numhask,
     optparse-generic,
diff --git a/src/Perf/Cycle.hs b/src/Perf/Cycle.hs
--- a/src/Perf/Cycle.hs
+++ b/src/Perf/Cycle.hs
@@ -10,30 +10,32 @@
 -- The measurement unit - a 'Cycle' - is one oscillation of the chip crystal as measured by the <https://en.wikipedia.org/wiki/Time_Stamp_Counter rdtsc> instruction which inspects the TSC register.
 --
 -- For reference, a computer with a frequency of 2 GHz means that one cycle is equivalent to 0.5 nanoseconds.
---
+--  
 module Perf.Cycle
   ( -- $setup
     Cycle
   , tick_
   , warmup
   , tick
-  , app
+  , tick'
   , tickIO
   , ticks
-  , qtick
   , ticksIO
-  , tickns
-  , force
-  , replicateM'
+  , ns
+  , tickWHNF
+  , tickWHNF'
+  , tickWHNFIO
+  , ticksWHNF
+  , ticksWHNFIO
   , average
   , deciles
   , percentile
   ) where
 
 import qualified Control.Foldl as L
-import Data.List
+import Data.List as List
 import Data.TDigest
-import NumHask.Prelude hiding (force)
+import NumHask.Prelude
 import System.CPUTime.Rdtsc
 import qualified Protolude
 
@@ -45,7 +47,6 @@
 -- >>> let f x = foldl' (+) 0 [1 .. x]
 --
 
-
 -- | an unwrapped Word64
 type Cycle = Word64
 
@@ -109,77 +110,80 @@
   ts <- replicateM n tick_
   pure $ average ts
 
--- | `tick f a` strictly applies a to f, and returns a (Cycle, f a)
+-- | tick where the arguments are lazy, so measurement may include evluation of thunks that may constitute f and/or a
+tick' :: (NFData b) => (a -> b) -> a -> IO (Cycle, b)
+tick' f a = do
+  !t <- rdtsc
+  !a' <- pure (force $ f a)
+  !t' <- rdtsc
+  pure (t' - t, a')
+
+-- | `tick f a` strictly evaluates f and a, then deeply evaluates f a, returning a (Cycle, f a)
 --
 -- >>> _ <- warmup 100
 -- >>> (cs, _) <- tick f a
 --
--- > one tick: 197012 cycles
--- > average over 1000: 10222.79 cycles -- 10 cycles per operation
--- > [min, 30th, median, 90th, 99th, max]:
--- > 1.002e4 1.011e4 1.013e4 1.044e4 1.051e4 2.623e4
-tick :: (a -> b) -> a -> IO (Cycle, b)
-tick f a = do
-  !t <- rdtsc
-  !a' <- pure (f a)
-  !t' <- rdtsc
-  pure (t' - t, a')
+-- > sum to 1000
+-- > first measure: 1202 cycles
+-- > second measure: 18 cycles
+--
+-- Note that feeding the same computation through tick twice will tend to kick off sharing (aka memoization aka let floating).  Given the importance of sharing to GHC optimisations this is the intended behaviour.  If you want to turn this off then see -fn--full-laziness (and maybe -fno-cse).
+tick :: (NFData b) => (a -> b) -> a -> IO (Cycle, b)
+tick !f !a = tick' f a
 
--- | evaluates and measures an `IO a`
+tickNoinline :: (NFData b) => (a -> b) -> a -> IO (Cycle, b)
+tickNoinline !f !a = tick' f a
+{-# NOINLINE tickNoinline #-}
+
+-- | measures and deeply evaluates an `IO a`
 --
 -- >>> (cs, _) <- tickIO (pure (f a))
 --
-tickIO :: IO a -> IO (Cycle, a)
+tickIO :: (NFData a) => IO a -> IO (Cycle, a)
 tickIO a = do
   t <- rdtsc
-  !a' <- a
+  !a' <- force <$> a
   t' <- rdtsc
   pure (t' - t, a')
 
--- | needs more testing
-app :: t -> () -> t
-app e () = e
-{-# NOINLINE app #-}
+tickIONoinline :: (NFData a) => IO a -> IO (Cycle, a)
+tickIONoinline = tickIO
+{-# NOINLINE tickIONoinline #-}
 
 -- | n measurements of a tick
 --
 -- returns a list of Cycles and the last evaluated f a
 --
--- GHC is very good as memoization, and any of the functions that measuring a computation multiple times are fraught.  When a computation actually gets memoized is an inexact science.  Current readings are:
---
--- > sum to 1000.0
--- > Perf.ticks n f a                        8.37e3 cycles
--- > Main.ticks n f a                        8.38e3 cycles
--- > Perf.ticksIO n (pure $ f a)             8.38e3 cycles
--- > Perf.qtick n f a                        8.38e3 cycles
--- > Main.qtick n f a                        8.38e3 cycles
--- > replicateM n (tick f a)                 8.37e3 cycles
--- > replicateM' n (tick f a)                9.74e3 cycles
--- > replicateM n (tickIO (pure (f a)))      1.21e4 cycles
--- > replicateM n (tick (app (f a)) ())      9.72e3 cycles
--- > replicateM n (tick identity (f n))      18.2 cycles
--- > replicateM n (tick (const (f a)) ())    9.71e3 cycles
--- > (replicateM n . tick f) <$> [1,10,100,1000,10000]:  16.3 16.2 16.3 16.2 16.2
--- > Perf.tickns n f [1,10,100,1000,10000]:  16.2 16.2 16.2 16.2 16.2
+-- GHC is very good at finding ways to share computation, and anything measuring a computation multiple times is a prime candidate for aggresive ghc treatment. Internally, ticks uses a noinline pragma and a noinline on tick to help reduce the chances of memoization, but this is an inexact science in the hands of he author, at least, so interpret with caution.
 --
+-- 
 -- >>> let n = 1000
 -- >>> (cs, fa) <- ticks n f a
 --
-ticks :: Int -> (a -> b) -> a -> IO ([Cycle], b)
-ticks n f a = do
-  ts <- replicateM' n (tick f a)
-  pure (fst <$> ts, snd $ last ts)
-{-# INLINE ticks #-}
-
--- | returns the 40th percentile measurement and the last evaluated f a
+-- Baseline speed can be highly senistive to the nature of the function trimmings.  Polymorphic functions can tend to be slightly slower, and functions with lambda expressions can experience dramatic slowdowns.
 --
--- >>> (c, fa) <- qtick n f a
+-- > fMono :: Int -> Int
+-- > fMono x = foldl' (+) 0 [1 .. x]
+-- > fPoly :: (Enum b, Num b, Additive b) => b -> b
+-- > fPoly x = foldl' (+) 0 [1 .. x]
+-- > fLambda :: Int -> Int
+-- > fLambda = \x -> foldl' (+) 0 [1 .. x]
 --
-qtick :: Int -> (a -> b) -> a -> IO (Double, b)
-qtick n f a = do
-  ts <- replicateM' n (tick f a)
-  pure (percentile 0.4 $ fst <$> ts, snd $ last ts)
-{-# INLINE qtick #-}
+-- > sum to 1000 n = 1000 prime run: 1.13e3
+-- > run                       first     2nd     3rd     4th     5th  40th %
+-- > ticks                    1.06e3     712     702     704     676    682 cycles
+-- > ticks (lambda)           1.19e3     718     682     684     678    682 cycles
+-- > ticks (poly)             1.64e3  1.34e3  1.32e3  1.32e3  1.32e3 1.31e3 cycles
+--
+ticks :: (NFData b) => Int -> (a -> b) -> a -> IO ([Cycle], b)
+ticks n0 f a = go f a n0 []
+  where
+    go f' a' n ts
+      | n <= 0 = pure (reverse ts, f a)
+      | otherwise = do
+          (t,_) <- tickNoinline f a
+          go f' a' (n - 1) (t:ts)
+{-# NOINLINE ticks #-}
 
 -- | n measuremenst of a tickIO
 --
@@ -187,35 +191,35 @@
 --
 -- >>> (cs, fa) <- ticksIO n (pure $ f a)
 --
-ticksIO :: Int -> IO a -> IO ([Cycle], a)
-ticksIO n a = do
-    cs <- replicateM n (tickIO a)
-    pure (fst <$> cs, last $ snd <$> cs)
+-- > ticksIO                     834     752     688     714     690    709 cycles
+-- > ticksIO (lambda)            822     690     720     686     688    683 cycles
+-- > ticksIO (poly)           1.01e3     688     684     682     712    686 cycles
+ticksIO :: (NFData a) => Int -> IO a -> IO ([Cycle], a)
+ticksIO n0 a = go a n0 []
+  where
+    go a' n ts
+      | n <= 0 = do
+            a'' <- a'
+            pure (reverse ts, a'')
+      | otherwise = do
+          (t,_) <- tickIONoinline a'
+          go a' (n - 1) (t:ts)
+{-# NOINLINE ticksIO #-}
 
--- | n measurements on each of a list of a's to be applied to f.
+-- | make a series of measurements on a list of a's to be applied to f, for a tick function.
 --
--- Currently memoizing it's ass off
+-- Tends to be fragile to sharing issues, but very useful to determine computation Order
 --
--- > tickns n f [1,10,100,1000]
+-- > ns ticks n f [1,10,100,1000]
 --
-tickns :: Int -> (a -> b) -> [a] -> IO ([[Cycle]], [b])
-tickns n f as = do
-  cs <- sequence $ ticks n f <$> as
+-- > sum to's [1,10,100,1000]
+-- > tickns n fMono:  17.8 23.5 100 678
+--
+ns :: (NFData b) => (a -> IO ([Cycle],b)) -> [a] -> IO ([[Cycle]], [b])
+ns t as = do
+  cs <- sequence $ t <$> as
   pure (fst <$> cs, snd <$> cs)
 
--- | extra oomph for those hard to reach evaluations
-force :: (NFData a) => a -> a
-force x = x `deepseq` x
-
--- | a replicateM with good attributes
-replicateM' :: Monad m => Int -> m a -> m [a]
-replicateM' n op' = go n []
-  where
-    go 0 acc = return $ reverse acc
-    go n' acc = do
-      x <- op'
-      go (n' - 1) (x : acc)
-
 -- | average of a Cycle foldable
 --
 -- > cAv <- average <$> ticks n f a
@@ -234,8 +238,60 @@
 
 -- | compute a percentile
 --
--- > c <- percentoile 0.4 <$> ticks n f a
+-- > c <- percentile 0.4 <$> ticks n f a
 --
 percentile :: (Functor f, Foldable f) => Double -> f Cycle -> Double
 percentile p xs = fromMaybe 0 $ quantile p (tdigest (fromIntegral <$> xs) :: TDigest 25)
 
+
+-- | WHNF version
+tickWHNF :: (a -> b) -> a -> IO (Cycle, b)
+tickWHNF !f !a = tickWHNF' f a
+
+tickWHNFNoinline :: (a -> b) -> a -> IO (Cycle, b)
+tickWHNFNoinline !f !a = tickWHNF' f a
+{-# NOINLINE tickWHNFNoinline #-}
+
+-- | WHNF version
+tickWHNF' :: (a -> b) -> a -> IO (Cycle, b)
+tickWHNF' f a = do
+  !t <- rdtsc
+  !a' <- pure (f a)
+  !t' <- rdtsc
+  pure (t' - t, a')
+
+-- | WHNF version
+tickWHNFIO :: IO a -> IO (Cycle, a)
+tickWHNFIO a = do
+  t <- rdtsc
+  !a' <- a
+  t' <- rdtsc
+  pure (t' - t, a')
+
+tickWHNFIONoinline :: IO a -> IO (Cycle, a)
+tickWHNFIONoinline = tickWHNFIO
+{-# NOINLINE tickWHNFIONoinline #-}
+
+-- | WHNF version
+ticksWHNF :: Int -> (a -> b) -> a -> IO ([Cycle], b)
+ticksWHNF n0 f a = go f a n0 []
+  where
+    go f' a' n ts
+      | n <= 0 = pure (reverse ts, f a)
+      | otherwise = do
+          (t,_) <- tickWHNFNoinline f a
+          go f' a' (n - 1) (t:ts)
+{-# NOINLINE ticksWHNF #-}
+
+-- | WHNF version
+ticksWHNFIO :: Int -> IO a -> IO ([Cycle], a)
+ticksWHNFIO n0 a = go a n0 []
+  where
+    go a' n ts
+      | n <= 0 = do
+            a'' <- a'
+            pure (reverse ts, a'')
+      | otherwise = do
+          (t,_) <- tickWHNFIONoinline a'
+          go a' (n - 1) (t:ts)
+{-# NOINLINE ticksWHNFIO #-}
