Commits

Anonymous committed 96f2b43

Changed the plotting of KDEs so that you can plot several graphs with the same X axis scale

The shared X axis is auto-scaled so that it encompasses the data from all the graphs.

  • Participants
  • Parent commits 9c9d715

Comments (0)

Files changed (2)

     , runAndAnalyse
     ) where
 
-import Control.Monad (replicateM_, when)
+import Control.Monad (replicateM_, when, liftM, (<=<))
 import Criterion.Analysis (OutlierVariance(..), classifyOutliers,
                            outlierVariance, noteOutliers)
 import Criterion.Config (Config(..), Plot(..), fromLJ)
 import Criterion.Measurement (getTime, runForAtLeast, secs, time_)
 import Criterion.Plot (plotWith, plotKDE, plotTiming)
 import Criterion.Types (Benchmarkable(..), Benchmark(..), bench, bgroup)
-import Data.Array.Vector ((:*:)(..), lengthU, mapU)
+import Data.Array.Vector ((:*:)(..), lengthU, mapU, foldlU)
+import Data.Maybe (mapMaybe)
+import Data.Monoid (getLast)
 import Statistics.Function (createIO)
 import Statistics.KernelDensity (epanechnikovPDF)
 import Statistics.RandomVariate (withSystemRandom)
 
 -- | Run a single benchmark and analyse its performance.
 runAndAnalyseOne :: Benchmarkable b => Config -> Environment -> String -> b
-                 -> IO ()
-runAndAnalyseOne cfg env desc b = do
+                 -> IO Sample
+runAndAnalyseOne cfg env _desc b = do
   times <- runBenchmark cfg env b
   let numSamples = lengthU times
-  plotWith Timing cfg $ \o -> plotTiming o desc times
-  plotWith KernelDensity cfg $ \o -> uncurry (plotKDE o desc)
-                                     (epanechnikovPDF 100 times)
   let ests = [mean,stdDev]
       numResamples = fromLJ cfgResamples cfg
   note cfg "bootstrapping with %d resamples\n" numResamples
   noteOutliers cfg (classifyOutliers times)
   note cfg "variance introduced by outliers: %.3f%%\n" (v * 100)
   note cfg "variance is %s by outliers\n" wibble
+  return times
   where bs :: String -> Estimate -> IO ()
         bs d e = note cfg "%s: %s, lb %s, ub %s, ci %.3f\n" d
                    (secs $ estPoint e)
                    (secs $ estLowerBound e) (secs $ estUpperBound e)
                    (estConfidenceLevel e)
 
+plotOne :: Config -> String -> Sample -> IO ()
+plotOne cfg desc times = do
+  plotWith Timing cfg $ \o -> plotTiming o desc times
+  plotWith KernelDensity cfg $ \o -> uncurry (plotKDE o desc Nothing)
+                                     (epanechnikovPDF 100 times)
+
+plotAll :: Config -> [(String, Sample)] -> IO ()
+plotAll cfg descTimes = sequence_ [do
+  plotWith Timing cfg $ \o -> plotTiming o desc times
+  plotWith KernelDensity cfg $ \o -> uncurry (plotKDE o desc extremes)
+                                            (epanechnikovPDF 100 times)
+            | (desc, times) <- descTimes]
+  where
+    extremes :: Maybe (Double, Double)
+    extremes = foldl minMaxMaybe2 Nothing $ mapMaybe (foldlU minMaxMaybe Nothing . snd) descTimes
+
+    minMaxMaybe :: Maybe (Double, Double) -> Double -> Maybe (Double, Double)
+    minMaxMaybe a b = minMaxMaybe2 a (b, b)
+
+    minMaxMaybe2 :: Maybe (Double, Double) -> (Double, Double) -> Maybe (Double, Double)
+    minMaxMaybe2 Nothing (xMin, xMax) = Just (xMin, xMax)
+    minMaxMaybe2 (Just (curMin, curMax)) (xMin, xMax) = Just (min xMin curMin, max xMax curMax)
+
+
 -- | Run, and analyse, one or more benchmarks.
 runAndAnalyse :: (String -> Bool) -- ^ A predicate that chooses
                                   -- whether to run a benchmark by its
               -> Environment
               -> Benchmark
               -> IO ()
-runAndAnalyse p cfg env = go ""
+runAndAnalyse p cfg env
+  = (case getLast $ cfgPlotSameAxis cfg of
+       Just True -> plotAll cfg
+       _ -> mapM_ (uncurry $ plotOne cfg)
+    ) <=< go ""
   where go pfx (Benchmark desc b)
             | p desc'   = do note cfg "\nbenchmarking %s\n" desc'
-                             runAndAnalyseOne cfg env desc' b
-            | otherwise = return ()
+                             x <- runAndAnalyseOne cfg env desc' b
+                             return [(desc', x)]
+            | otherwise = return []
             where desc' = prefix pfx desc
-        go pfx (BenchGroup desc bs) = mapM_ (go (prefix pfx desc)) bs
+        go pfx (BenchGroup desc bs) = liftM concat $ mapM (go (prefix pfx desc)) bs
         prefix ""  desc = desc
         prefix pfx desc = pfx ++ '/' : desc

Criterion/Plot.hs

 -- | Plot kernel density estimate.
 plotKDE :: PlotOutput           -- ^ The kind of output desired.
         -> String               -- ^ Benchmark name.
+        -> Maybe (Double, Double) -- ^ Minimum extremities for x-axis
         -> Points               -- ^ Points at which KDE was computed.
         -> UArr Double          -- ^ Kernel density estimates.
         -> IO ()
 
-plotKDE CSV desc points pdf = do
+plotKDE CSV desc _exs points pdf = do
   writeTo (mangle $ printf "%s densities.csv" desc) $ \h -> do
     putRow h ["execution time", "probability"]
     forM_ (zip (fromU pdf) (fromU (fromPoints points))) $ \(x, y) ->
       putRow h [show x, show y]
 
 #ifdef HAVE_CHART
-plotKDE (PDF x y) desc points pdf =
-  renderableToPDFFile (renderKDE desc points pdf) x y
+plotKDE (PDF x y) desc exs points pdf =
+  renderableToPDFFile (renderKDE desc exs points pdf) x y
                       (mangle $ printf "%s densities %dx%d.pdf" desc x y)
 
-plotKDE (PNG x y) desc points pdf =
-  renderableToPNGFile (renderKDE desc points pdf) x y
+plotKDE (PNG x y) desc exs points pdf =
+  renderableToPNGFile (renderKDE desc exs points pdf) x y
                       (mangle $ printf "%s densities %dx%d.png" desc x y)
 
-plotKDE (SVG x y) desc points pdf =
-  renderableToSVGFile (renderKDE desc points pdf) x y
+plotKDE (SVG x y) desc exs points pdf =
+  renderableToSVGFile (renderKDE desc exs points pdf) x y
                       (mangle $ printf "%s densities %dx%d.svg" desc x y)
 
-plotKDE (Window x y) desc points pdf =
-    renderableToWindow (renderKDE desc points pdf) x y
+plotKDE (Window x y) desc exs points pdf =
+    renderableToWindow (renderKDE desc exs points pdf) x y
 #else
-plotKDE output _desc _points _pdf =
+plotKDE output _desc _exs _points _pdf =
   printError "ERROR: output type %s not supported on this platform\n"
              (show output)
 #endif
          $ plot_bars_spacing ^= BarsFixGap 0
          $ defaultPlotBars
 
-renderKDE :: String -> Points -> UArr Double -> Renderable ()
-renderKDE desc points pdf = toRenderable layout
+renderKDE :: String -> Maybe (Double, Double) -> Points -> UArr Double -> Renderable ()
+renderKDE desc possibleOtherExtremities points pdf = toRenderable layout
   where
     layout = layout1_title ^= "Densities of execution times for \"" ++
                               desc ++ "\""
     leftAxis = laxis_title ^= "estimate of probability density"
              $ defaultLayoutAxis
 
-    bottomAxis = laxis_generate ^= autoScaledAxis secAxis
+    bottomAxis = laxis_generate ^= semiAutoScaledAxis secAxis
                $ laxis_title ^= "execution time"
                $ defaultLayoutAxis
 
+    semiAutoScaledAxis opts ps = autoScaledAxis opts (otherExtremities ++ ps)
+    otherExtremities = maybe [] (\(x, y) -> [x, y]) possibleOtherExtremities
+
     info = plot_lines_values ^= [zip (fromU (fromPoints points)) (fromU spdf)]
          $ defaultPlotLines