packages feed

javelin-0.1.2.0: scripts/bench-report.hs

-- This script has been forked from:
-- https://github.com/haskell-perf/sets/blob/master/Report.hs
{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}
module Main (main) where

import Data.Function        ( on )
import Data.List            ( groupBy, intercalate, nub )
import System.Environment   ( getArgs )
import Text.CSV             ( parseCSVFromFile )
import Text.Printf          ( printf )

main :: IO ()
main = do
  from:to:_ <- getArgs
  reportFromCsv from to

reportFromCsv :: FilePath -> FilePath -> IO ()
reportFromCsv from to = do
  result <- parseCSVFromFile from
  case result of
    Right (_:rows) -> do
      writeFile to
        (unlines
           (map
              format
              (filter
                 (not . all (all null))
                 (groupBy (on (==) (takeWhile (/= '/') . concat . take 1)) rows))))
    _ -> error "Couldn't parse csv"

format :: [[String]] -> String
format rows =
  ("## " ++ takeWhile (/= '/') (concat (concat (take 1 (drop 1 rows))))) ++
  "\n\n" ++
  unlines
    [ "|Name|" ++ intercalate "|" scales ++ "|"
    , "|" ++ concat (replicate (1 + length scales) "---|")
    ] ++
  unlines
    (map
       (\name ->
          "|" ++ name ++ "|" ++ intercalate "|" (valuesByName name) ++ "|")
       names)
  where
    valuesByName name =
      map
        (\row@(_:avg:_) ->
           let scale = rowScale row
           in float (valuesByScale scale) (read avg))
        (filter ((== name) . rowName) rows)
    valuesByScale scale =
      map (\(_:avg:_) -> read avg) (filter ((== scale) . rowScale) rows)
    names = nub (map rowName rows)
    scales = nub (map rowScale rows)
    rowName row =
      let s =
            takeWhile
              (/= ':')
              (dropWhile (== '/') (dropWhile (/= '/') (concat (take 1 row))))
      in s
    rowScale row =
      let scale = dropWhile (== ':') (dropWhile (/= ':') (concat (take 1 row)))
      in scale

float :: [Double] -> Double -> String
float others x = let (scale, ext) = secs (mean others)
                 in with (x * scale) ext

-- | Convert a number of seconds to a string.  The string will consist
-- of four decimal places, followed by a short description of the time
-- units.
secs :: Double -> (Double, String)
secs k
    | k >= 1     = 1    `pair` "s"
    | k >= 1e-3  = 1e3  `pair` "ms"
    | k >= 1e-6  = 1e6  `pair` "μs"
    | k >= 1e-9  = 1e9  `pair` "ns"
    | k >= 1e-12 = 1e12 `pair` "ps"
    | k >= 1e-15 = 1e15 `pair` "fs"
    | k >= 1e-18 = 1e18 `pair` "as"
    | otherwise = error "Bad scale"
  where pair= (,)

with :: Double -> String -> String
with (t :: Double) (u :: String)
    | t >= 1e9  = printf "%.4g %s" t u
    | t >= 1e3  = printf "%.0f %s" t u
    | t >= 1e2  = printf "%.1f %s" t u
    | t >= 1e1  = printf "%.2f %s" t u
    | otherwise = printf "%.3f %s" t u

-- | Simple rolling average.
mean :: [Double] -> Double
mean =
    snd .
    foldr
        (\x (cnt,avg) ->
              ( cnt + 1
              , (x + avg * cnt) / (cnt + 1)))
        (0, 0)