bench-show-0.2.0: lib/BenchShow/Graph.hs
-- |
-- Module : BenchShow.Graph
-- Copyright : (c) 2018 Composewell Technologies
--
-- License : BSD3
-- Maintainer : harendra.kumar@gmail.com
-- Stability : experimental
-- Portability : GHC
--
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TupleSections #-}
module BenchShow.Graph
(
graph
) where
import Control.Arrow (second)
import Control.Monad (forM_, when)
import Control.Monad.Trans.State.Lazy (get, put)
import Data.Maybe (fromMaybe)
import Graphics.Rendering.Chart.Easy
import Graphics.Rendering.Chart.Backend.Diagrams
import BenchShow.Common
-------------------------------------------------------------------------------
-- Benchmarking field specific handling
-------------------------------------------------------------------------------
-- XXX need the ability to specify Units in the scale
yindexes :: Maybe (Double, Double)
-> Maybe FieldTick
-> Double
-> Maybe [Double]
yindexes fieldRange granularity multiplier =
case (fieldRange, granularity) of
(Just (rangeMin, rangeMax), Just g) ->
let range = rangeMax - rangeMin
(size, count) =
case g of
TickSize n ->
(fromIntegral n, round $ range / fromIntegral n)
TickCount n -> (range / fromIntegral n, n)
in let size' = size / multiplier
rmin = rangeMin / multiplier
in Just $ take (count + 1) [rmin, rmin + size'..]
_ -> Nothing
-------------------------------------------------------------------------------
transformColumns :: [ReportColumn] -> [ReportColumn]
transformColumns columns =
if length columns == 1
-- workaround for a bug that renders the plot badly when using
-- a single cluster in the bar chart.
then columns ++ [ReportColumn
{ colName = ""
, colUnit = RelativeUnit "" 1
, colValues = []
}]
else columns
genGroupGraph :: RawReport -> Config -> IO ()
genGroupGraph RawReport{..} cfg@Config{..} = do
let outputFile = fromMaybe undefined reportOutputFile
fieldRange = getFieldRange reportIdentifier cfg
granularity = getFieldTick reportIdentifier cfg
-- XXX assert that the unit for all columns is the same
RelativeUnit ulabel multiplier = colUnit (head reportColumns)
replaceMu 'μ' = 'u'
replaceMu x = x
unitLabel = map replaceMu ulabel
columns = transformColumns reportColumns
diffStr =
if length reportColumns > 1
then diffString presentation diffStrategy
else Nothing
atitle = makeTitle reportIdentifier diffStr cfg
toFile def outputFile $ do
layout_title .= atitle
layout_title_style . font_size .= 25
layout_x_axis . laxis_generate .=
autoIndexAxis (map (map replaceMu . colName) columns)
layout_x_axis . laxis_style . axis_label_style . font_size .= 16
layout_y_axis . laxis_style . axis_label_style . font_size .= 14
layout <- get
case _layout_legend layout of
Nothing -> return ()
Just style@LegendStyle{..} -> do
let s = style { _legend_plot_size = 22
-- , _legend_margin = 40
-- This is not available in versions <= 1.8.2
-- , _legend_position = LegendBelow
, _legend_label_style = _legend_label_style
{ _font_size = 14 }
}
put $ layout { _layout_legend = Just s }
-- layout_y_axis . laxis_override .= axisGridAtTicks
let modifyLabels ad = ad {
_axis_labels = map (map (second (++ " " ++ unitLabel)))
(_axis_labels ad)
}
when (presentation /= Fields) $
layout_y_axis . laxis_override .= modifyLabels
case yindexes fieldRange granularity multiplier of
Nothing -> return ()
Just indexes ->
layout_y_axis . laxis_override .= \_ ->
makeAxis (let f = floor :: Double -> Int
in map ((++ " " ++ unitLabel) . show . f))
(indexes, [], [])
plot $ fmap plotBars $ bars reportRowIds
$ (addIndexes $ map colValues columns)
-- | Presents the benchmark results in a CSV input file as graphical bar charts
-- according to the provided configuration. The first parameter is the input
-- file name, the second parameter is the name prefix for the output SVG image
-- file(s). One or more output files may be generated depending on the
-- 'Presentation' setting. The last parameter is the configuration to
-- customize the graph, you can start with 'defaultConfig' as the base and
-- override any of the fields that you may want to change.
--
-- For example:
--
-- @
-- graph "bench-results.csv" "output-graph" 'defaultConfig'
-- @
--
-- @since 0.2.0
graph :: FilePath -> FilePath -> Config -> IO ()
graph inputFile outputFile cfg@Config{..} = do
let dir = fromMaybe "." outputDir
(csvlines, fields) <- prepareToReport inputFile cfg
(runs, matrices) <- prepareGroupMatrices cfg csvlines fields
case presentation of
Groups style ->
forM_ fields $
reportComparingGroups style dir (Just outputFile)
GraphicalChart runs cfg
genGroupGraph matrices
Fields -> do
forM_ matrices $
reportPerGroup dir (Just outputFile) GraphicalChart
cfg genGroupGraph
Solo ->
let funcs = map
(\mx -> reportComparingGroups Absolute dir
(Just $ outputFile ++ "-" ++ groupName mx)
GraphicalChart runs cfg genGroupGraph [mx])
matrices
in sequence_ $ funcs <*> fields