From 72170d885a326065d935d2eb61b985c0dbeef812 Mon Sep 17 00:00:00 2001 From: Andrew Darqui Date: Sun, 10 Jul 2016 02:44:17 -0400 Subject: [PATCH 1/6] successfully runs multiple commands --- src/Main.hs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/Main.hs b/src/Main.hs index 6d461e7..e902b50 100644 --- a/src/Main.hs +++ b/src/Main.hs @@ -15,20 +15,22 @@ import qualified System.IO.Silently as Silently import qualified Turtle data Options = Options - { command :: Text - , mode :: Criterion.Mode - } + { commands :: [Text] + , mode :: Criterion.Mode + } deriving (Show) parser :: Parser Options parser = Options - <$> Turtle.argText "command" "The command line to benchmark" + <$> Turtle.argRead "commands" "The command line to benchmark" <*> Criterion.parseWith Criterion.defaultConfig main :: IO () main = do + putStrLn "hi" o <- Turtle.options "Command-line tool to benchmark other programs" parser - let io = Turtle.shells (command o) empty - let benchmark = Criterion.nfIO (Silently.hSilence [IO.stdout, IO.stderr] io) - let name = Text.unpack (command o) - Criterion.runMode (mode o) [ Criterion.bench name benchmark ] + print o + let ios = map (\o' -> Turtle.shells o' empty) (commands o) + let benchmarks = map (\io -> Criterion.nfIO (Silently.hSilence [IO.stdout, IO.stderr] io)) ios + let benches = map (\(command, benchmark) -> Criterion.bench (Text.unpack command) benchmark) $ zip (commands o) benchmarks + Criterion.runMode (mode o) [Criterion.bgroup "commands" benches] From f7efa5225eda160ca1cf978dc0147db4e1902e3c Mon Sep 17 00:00:00 2001 From: Andrew Darqui Date: Sun, 10 Jul 2016 02:44:22 -0400 Subject: [PATCH 2/6] gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0caeeda --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/.stack-work From dc0ac5f3c46d40aad42d6d85af8f92244396f540 Mon Sep 17 00:00:00 2001 From: Andrew Darqui Date: Sun, 10 Jul 2016 03:17:38 -0400 Subject: [PATCH 3/6] it works --- src/Main.hs | 45 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 37 insertions(+), 8 deletions(-) diff --git a/src/Main.hs b/src/Main.hs index e902b50..2e9d15a 100644 --- a/src/Main.hs +++ b/src/Main.hs @@ -10,27 +10,56 @@ import qualified Criterion import qualified Criterion.Main as Criterion import qualified Criterion.Main.Options as Criterion import qualified Data.Text as Text +import qualified Text.Read as Read import qualified System.IO as IO import qualified System.IO.Silently as Silently import qualified Turtle +data Cmd + = Command Text + | Commands [Text] + deriving (Show) + +instance Read Cmd where + readsPrec d t = + -- First attempt to read Commands: ["cmd", "cmd", ...] + case (Read.readMaybe t :: Maybe [Text]) of + Just t' -> [(Commands t', "")] + -- If that fails, fallback to Command + Nothing -> [(Command $ Text.pack t, "")] + data Options = Options - { commands :: [Text] + { cmd :: Cmd , mode :: Criterion.Mode } deriving (Show) parser :: Parser Options parser = Options - <$> Turtle.argRead "commands" "The command line to benchmark" + <$> Turtle.argRead "command(s)" "The command line(s) to benchmark" <*> Criterion.parseWith Criterion.defaultConfig main :: IO () main = do - putStrLn "hi" o <- Turtle.options "Command-line tool to benchmark other programs" parser - print o - let ios = map (\o' -> Turtle.shells o' empty) (commands o) - let benchmarks = map (\io -> Criterion.nfIO (Silently.hSilence [IO.stdout, IO.stderr] io)) ios - let benches = map (\(command, benchmark) -> Criterion.bench (Text.unpack command) benchmark) $ zip (commands o) benchmarks - Criterion.runMode (mode o) [Criterion.bgroup "commands" benches] + case (cmd o) of + Command command -> benchCommand command (mode o) + Commands commands -> benchCommands commands (mode o) + +benchCommands :: [Text] -> Criterion.Mode -> IO () +benchCommands commands mode = do + let benches = map (\command -> buildBench command mode) commands + Criterion.runMode mode [Criterion.bgroup "bench" benches] + +benchCommand :: Text -> Criterion.Mode -> IO () +benchCommand command mode = do + let bench = buildBench command mode + Criterion.runMode mode [ bench ] + + +buildBench :: Text -> Criterion.Mode -> Criterion.Benchmark +buildBench command mode = do + let io = Turtle.shells command empty + let benchmark = Criterion.nfIO (Silently.hSilence [IO.stdout, IO.stderr] io) + let bench = Criterion.bench (Text.unpack command) benchmark + bench From 5e5418a3122b4d59487736e04c7b556f07d4c207 Mon Sep 17 00:00:00 2001 From: Andrew Darqui Date: Sun, 10 Jul 2016 03:22:18 -0400 Subject: [PATCH 4/6] supports one command: bench "command" or multiple commands: bench '["command1", "command2"]' --- src/Main.hs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/Main.hs b/src/Main.hs index 2e9d15a..141eef4 100644 --- a/src/Main.hs +++ b/src/Main.hs @@ -1,4 +1,5 @@ {-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RecordWildCards #-} module Main where @@ -21,7 +22,7 @@ data Cmd deriving (Show) instance Read Cmd where - readsPrec d t = + readsPrec _ t = -- First attempt to read Commands: ["cmd", "cmd", ...] case (Read.readMaybe t :: Maybe [Text]) of Just t' -> [(Commands t', "")] @@ -43,22 +44,21 @@ main :: IO () main = do o <- Turtle.options "Command-line tool to benchmark other programs" parser case (cmd o) of - Command command -> benchCommand command (mode o) - Commands commands -> benchCommands commands (mode o) + Command command -> benchCommand command o + Commands commands -> benchCommands commands o -benchCommands :: [Text] -> Criterion.Mode -> IO () -benchCommands commands mode = do - let benches = map (\command -> buildBench command mode) commands +benchCommands :: [Text] -> Options -> IO () +benchCommands commands opts@Options{..} = do + let benches = map (\command -> buildBench command opts) commands Criterion.runMode mode [Criterion.bgroup "bench" benches] -benchCommand :: Text -> Criterion.Mode -> IO () -benchCommand command mode = do - let bench = buildBench command mode +benchCommand :: Text -> Options -> IO () +benchCommand command opts@Options{..} = do + let bench = buildBench command opts Criterion.runMode mode [ bench ] - -buildBench :: Text -> Criterion.Mode -> Criterion.Benchmark -buildBench command mode = do +buildBench :: Text -> Options -> Criterion.Benchmark +buildBench command Options{..} = do let io = Turtle.shells command empty let benchmark = Criterion.nfIO (Silently.hSilence [IO.stdout, IO.stderr] io) let bench = Criterion.bench (Text.unpack command) benchmark From c3f6835389d87b989229d68d3d0963700fd78095 Mon Sep 17 00:00:00 2001 From: Andrew Darqui Date: Fri, 29 Jul 2016 17:27:45 -0400 Subject: [PATCH 5/6] Code cleanup, as suggested by Gabriel Gonzalez. The new usage of bench for multiple commands: ``` bench cmd1 cmd2 cmd3 ... cmdN ``` --- src/Main.hs | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/src/Main.hs b/src/Main.hs index 141eef4..e9dcd43 100644 --- a/src/Main.hs +++ b/src/Main.hs @@ -11,41 +11,27 @@ import qualified Criterion import qualified Criterion.Main as Criterion import qualified Criterion.Main.Options as Criterion import qualified Data.Text as Text -import qualified Text.Read as Read import qualified System.IO as IO import qualified System.IO.Silently as Silently import qualified Turtle -data Cmd - = Command Text - | Commands [Text] - deriving (Show) - -instance Read Cmd where - readsPrec _ t = - -- First attempt to read Commands: ["cmd", "cmd", ...] - case (Read.readMaybe t :: Maybe [Text]) of - Just t' -> [(Commands t', "")] - -- If that fails, fallback to Command - Nothing -> [(Command $ Text.pack t, "")] - data Options = Options - { cmd :: Cmd + { cmd :: [Text] , mode :: Criterion.Mode } deriving (Show) parser :: Parser Options parser = Options - <$> Turtle.argRead "command(s)" "The command line(s) to benchmark" + <$> some (Turtle.argText "command(s)" "The command line(s) to benchmark") <*> Criterion.parseWith Criterion.defaultConfig main :: IO () main = do o <- Turtle.options "Command-line tool to benchmark other programs" parser case (cmd o) of - Command command -> benchCommand command o - Commands commands -> benchCommands commands o + [command] -> benchCommand command o + commands -> benchCommands commands o benchCommands :: [Text] -> Options -> IO () benchCommands commands opts@Options{..} = do From 0bf60095d19f30896764008138e63ae120147a00 Mon Sep 17 00:00:00 2001 From: Andrew Darqui Date: Fri, 29 Jul 2016 17:33:18 -0400 Subject: [PATCH 6/6] README example --- README.md | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 42e2373..cf316d9 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,31 @@ std dev 47.69 μs (40.09 μs .. 57.91 μs) variance introduced by outliers: 81% (severely inflated) ``` -All output from the command being benchmarked is discarded +All output from the command being benchmarked is discarded. + +Multiple commands are also supported: + +```bash +$ bench id ls "sleep 0.1" +benchmarking bench/id +time 4.798 ms (4.764 ms .. 4.833 ms) + 0.999 R² (0.998 R² .. 1.000 R²) +mean 4.909 ms (4.879 ms .. 4.953 ms) +std dev 104.6 μs (78.91 μs .. 135.7 μs) + +benchmarking bench/ls +time 2.941 ms (2.889 ms .. 3.006 ms) + 0.996 R² (0.992 R² .. 0.998 R²) +mean 3.051 ms (3.015 ms .. 3.094 ms) +std dev 129.7 μs (104.3 μs .. 178.3 μs) +variance introduced by outliers: 25% (moderately inflated) + +benchmarking bench/sleep 0.1 +time 109.9 ms (108.5 ms .. 111.0 ms) + 1.000 R² (1.000 R² .. 1.000 R²) +mean 109.2 ms (108.5 ms .. 109.7 ms) +std dev 903.0 μs (676.4 μs .. 1.212 ms) +``` You can also output an HTML file graphing the distribution of timings by using the `--output` flag: