### Install Dependencies Source: https://github.com/bencheeorg/benchee/blob/main/README.md Use this command to install project dependencies. ```shell mix deps.get ``` -------------------------------- ### Suite-Level Setup and Teardown Outside Benchee Source: https://github.com/bencheeorg/benchee/blob/main/_autodocs/hooks-and-lifecycle.md Shows how to perform suite-level setup and teardown using standard Elixir code before and after calling `Benchee.run/2`. ```elixir # Suite-level setup {:ok, _} = Application.ensure_all_started(:my_app) setup_database() Benchee.run( %{ "operation" => fn -> operation() end } ) # Suite-level teardown cleanup_database() ``` -------------------------------- ### Suite Hooks for Setup and Teardown Source: https://github.com/bencheeorg/benchee/blob/main/README.md Execute setup and teardown functions before and after the entire benchmarking suite. This is useful for preparing or cleaning up resources used by all benchmarks. ```elixir your_setup() Benchee.run(%{"Awesome stuff" => fn -> magic end}) your_teardown() ``` -------------------------------- ### Formatter Options Format Examples Source: https://github.com/bencheeorg/benchee/blob/main/_autodocs/types.md Provides examples of how to specify formatters, including using only the module, module with options, a function, or a mix of both. ```elixir # Module only formatters: [Benchee.Formatters.Console] # Module with options formatters: [ {Benchee.Formatters.Console, extended_statistics: true} ] # Function formatters: [ fn suite -> IO.inspect(suite) end ] # Mixed formatters: [ {Benchee.Formatters.Console, comparison: false}, fn suite -> write_to_file(suite) end ] ``` -------------------------------- ### before_scenario Hook Example Source: https://github.com/bencheeorg/benchee/blob/main/README.md Demonstrates using a before_scenario hook to start an expensive resource and pass it to the scenario. The hook receives the scenario input and returns a modified input including the resource. ```elixir Benchee.run( %{ "foo" => { fn {input, resource} -> foo(input, resource) end, before_scenario: fn {input, resource} -> resource = alter_resource(resource) {input, resource} end }, "bar" => fn {input, resource} -> bar(input, resource) end }, inputs: %{"input 1" => 1}, before_scenario: fn input -> resource = start_expensive_resource() {input, resource} end ) ``` -------------------------------- ### Basic Benchee Usage Example Source: https://github.com/bencheeorg/benchee/blob/main/README.md Run a benchmark comparing two functions: `flat_map` and `map.flatten`. This example demonstrates how to define functions to benchmark and initiate the run. ```elixir list = Enum.to_list(1..10_000) map_fun = fn i -> [i, i * i] end Benchee.run( %{ "flat_map" => fn -> Enum.flat_map(list, map_fun) end, "map.flatten" => fn -> list |> Enum.map(map_fun) |> List.flatten() end } ) ``` -------------------------------- ### Input Options Format Examples Source: https://github.com/bencheeorg/benchee/blob/main/_autodocs/types.md Demonstrates two recommended ways to provide input data for benchmarks: as a map (preserving order in modern Elixir) or as a list of tuples. ```elixir # As a map (recommended - preserves order in modern Elixir) inputs: %{ "small" => Enum.to_list(1..10), "large" => Enum.to_list(1..10_000) } # As a list of tuples inputs: [ {"small", Enum.to_list(1..10)}, {"large", Enum.to_list(1..10_000)} ] ``` -------------------------------- ### Database Setup/Teardown Hook Pattern Source: https://github.com/bencheeorg/benchee/blob/main/_autodocs/hooks-and-lifecycle.md Example of using `before_scenario` and `after_scenario` hooks to manage database state for a benchmark. It sets up a test user before the scenario and cleans it up afterward. ```elixir Benchee.run( %{ "query" => fn user_id -> User.find(user_id) end }, inputs: %{ "user1" => 1, "user2" => 2 }, before_scenario: fn user_id -> # Setup: create test user in database {:ok, user} = create_user(user_id) user_id end, after_scenario: fn user_id -> # Cleanup: delete test user delete_user(user_id) end ) ``` -------------------------------- ### Add Setup/Cleanup Hooks Source: https://github.com/bencheeorg/benchee/blob/main/_autodocs/README.md Integrates setup and cleanup functions into the benchmarking lifecycle using `before_scenario` and `after_scenario` hooks. These functions execute without affecting measurements. ```elixir Benchee.run(jobs, before_scenario: fn input -> setup(input); input end, after_scenario: fn input -> cleanup(input) end ) ``` -------------------------------- ### Complete Hook Execution Example Source: https://github.com/bencheeorg/benchee/blob/main/README.md Demonstrates the order of execution for global and local hooks, including before_scenario, before_each, after_each, and after_scenario hooks, along with their arguments. ```elixir suite_set_up() Benchee.run( %{ "foo" => { fn input -> foo(input) end, before_scenario: fn input -> local_before_scenario(input) input + 1 end, before_each: fn input -> local_before_each(input) input + 1 end, after_each: fn value -> local_after_each(value) end, after_scenario: fn input -> local_after_scenario(input) end }, "bar" => fn input -> bar(input) end }, inputs: %{"input 1" => 1}, before_scenario: fn input -> global_before_scenario(input) input + 1 end, before_each: fn input -> global_before_each(input) input + 1 end, after_each: fn value -> global_after_each(value) end, after_scenario: fn input -> global_after_scenario(input) end ) suite_tear_down() ``` -------------------------------- ### Before/After Hooks for Scenario Setup and Cleanup Source: https://github.com/bencheeorg/benchee/blob/main/_autodocs/examples.md Utilizes `before_scenario`, `after_scenario`, and `before_each` hooks for setup and cleanup tasks around measurements. These hooks are called per function and input combination. ```elixir defmodule DatabaseBench do def run do Benchee.run( %{ "find_user" => fn user_id -> User.find(user_id) end, "find_with_posts" => fn user_id -> User.find_with_posts(user_id) end }, inputs: %{"user_1" => 1, "user_100" => 100}, before_scenario: fn user_id -> # Called once per (function + input) combination {:ok, user} = Fixtures.create_user(user_id) user_id end, after_scenario: fn user_id -> # Cleanup after scenario complete Fixtures.delete_user(user_id) end, before_each: fn user_id -> # Clear query cache before each measurement QueryCache.clear() user_id end ) end end DatabaseBench.run() ``` -------------------------------- ### Complete Hook Execution Order Example Source: https://github.com/bencheeorg/benchee/blob/main/_autodocs/hooks-and-lifecycle.md Demonstrates the detailed execution flow of global and local hooks for multiple scenarios, including warmup and measurement iterations. Shows how return values from 'before' hooks are chained as input to subsequent hooks. ```elixir suite_setup() Benchee.run( %{ "foo" => { fn input -> foo(input) end, before_scenario: fn input -> local_bs(input); input + 1 end, before_each: fn input -> local_be(input); input + 1 end, after_each: fn result -> local_ae(result) end, after_scenario: fn input -> local_as(input) end }, "bar" => fn input -> bar(input) end }, inputs: %{"input1" => 1}, before_scenario: fn input -> global_bs(input); input + 1 end, before_each: fn input -> global_be(input); input + 1 end, after_each: fn result -> global_ae(result) end, after_scenario: fn input -> global_as(input) end ) suite_teardown() ``` ```text suite_setup() ## FOO SCENARIO global_bs(1) # Global before_scenario receives input=1 local_bs(2) # Local before_scenario receives 2 (from global returning 1+1) # Now we have input=3 (from local returning 2+1) [WARMUP ITERATIONS] global_be(3) # Before warmup, before each local_be(4) # Receives 4 (from global returning 3+1) foo(5) # Function receives 5 (from local returning 4+1), returns some_value local_ae(some_value) # After local global_ae(some_value) # After global # ... repeated for each warmup iteration [MEASUREMENT ITERATIONS] global_be(3) # Before each measurement local_be(4) # foo(5) # Measured execution local_ae(some_value) # global_ae(some_value) # # ... repeated for time configured duration local_as(3) # Local after_scenario receives input=3 global_as(3) # Global after_scenario receives input=3 ## BAR SCENARIO global_bs(1) # No local hooks for bar # Input = 2 (from global) [WARMUP & MEASUREMENT - similar structure, only global hooks] global_be(2) bar(3) global_ae(return_value) global_as(2) # Only global after_scenario suite_teardown() ``` -------------------------------- ### Load Option Format Examples Source: https://github.com/bencheeorg/benchee/blob/main/_autodocs/types.md Illustrates how to load previously saved benchmark results. The default is `false` (no loading), but it can be a single file path or a list of file paths with glob support. ```elixir load: false # Default, no loading load: "my_benchmark.benchee" # Single file load: ["baseline.benchee", "branch-*.benchee"] # Multiple files with glob support ``` -------------------------------- ### Scenario Creation Example Source: https://github.com/bencheeorg/benchee/blob/main/_autodocs/inputs-and-data.md Illustrates how Benchee creates scenarios by combining defined jobs (functions) with specified inputs. Each unique job-input pair forms a scenario. ```elixir jobs = %{ "foo" => fn x -> ... end, "bar" => fn x -> ... end } inputs = %{ "input_a" => value_a, "input_b" => value_b } Benchee.run(jobs, inputs: inputs) ``` -------------------------------- ### Benchee To Benchmark Type Examples Source: https://github.com/bencheeorg/benchee/blob/main/_autodocs/types.md Illustrates the 'To Benchmark' type, which can be a plain benchmarking function or a tuple containing a function and local hook options. ```elixir # Plain function fn -> 1 + 1 end # Function with local hooks { fn input -> input * 2 end, before_each: fn input -> Integer.parse(input) |> elem(0) end, after_each: fn result -> result end } ``` -------------------------------- ### Time Unit Conversion Examples Source: https://github.com/bencheeorg/benchee/blob/main/_autodocs/inputs-and-data.md Demonstrates Benchee's automatic time unit conversion from raw microseconds to appropriate units (ns, μs, ms, s) for display. ```elixir # Raw values in microseconds (μs) microsecond = 1.0 millisecond = 1000.0 second = 1_000_000.0 nanosecond = 0.001 # Display conversion [Benchee.Conversion.Duration] # Automatically selects: ns, μs, ms, or s # based on magnitude and unit_scaling strategy 1.5 -> "1.5 μs" 1500.0 -> "1.5 ms" 1_500_000.0 -> "1.5 s" ``` -------------------------------- ### Profile Option Format Examples Source: https://github.com/bencheeorg/benchee/blob/main/_autodocs/types.md Demonstrates how to configure the `profile_after` option for profiling benchmark runs. It can be disabled, enabled with the default profiler, or set to a specific profiler with optional arguments. ```elixir profile_after: false # Default, no profiling profile_after: true # Enable default profiler (eprof) profile_after: :fprof # Specific profiler profile_after: {:fprof, [sort: :own]} # Profiler with options ``` -------------------------------- ### before_each Hook Example Source: https://github.com/bencheeorg/benchee/blob/main/README.md Shows how to use a before_each hook to prepare data for each invocation of a benchmarking function. This hook receives the input from before_scenario and its return value becomes the input for the benchmarking function. ```elixir Benchee.run( %{ "bar" => fn record -> bar(record) end }, inputs: %{ "record id" => 1}, before_each: fn input -> get_from_db(input) end ) ``` -------------------------------- ### Memory Unit Conversion Examples Source: https://github.com/bencheeorg/benchee/blob/main/_autodocs/inputs-and-data.md Illustrates Benchee's automatic memory unit conversion from raw kilobytes to appropriate units (B, KB, MB, GB) for display. ```elixir # Raw values in kilobytes (KB) kilobyte = 1.0 megabyte = 1024.0 gigabyte = 1024 * 1024.0 # Display conversion [Benchee.Conversion.Memory] # Automatically selects: B, KB, MB, or GB 512.0 -> "512 KB" 1024.0 -> "1 MB" 1048576.0 -> "1 GB" ``` -------------------------------- ### Run Benchee with HTML and Console Formatters Source: https://github.com/bencheeorg/benchee/blob/main/README.md Use this snippet to run Benchee with both HTML and Console formatters. Ensure the benchee_html plugin is installed. The HTML output is saved to a specified file. ```elixir list = Enum.to_list(1..10_000) map_fun = fn i -> [i, i * i] end Benchee.run( %{ "flat_map" => fn -> Enum.flat_map(list, map_fun) end, "map.flatten" => fn -> list |> Enum.map(map_fun) |> List.flatten end }, formatters: [ {Benchee.Formatters.HTML, file: "samples_output/my.html"}, Benchee.Formatters.Console ] ) ``` -------------------------------- ### Save Option Format Example Source: https://github.com/bencheeorg/benchee/blob/main/_autodocs/types.md Shows how to configure the 'save' option to persist benchmark results. The default is `false` (no saving), but it can be set to a map containing the path and an optional tag. ```elixir save: false # Default, no saving save: [ path: "my_benchmark.benchee", tag: "baseline" ] ``` -------------------------------- ### Function-Specific Hooks with Local Configuration Source: https://github.com/bencheeorg/benchee/blob/main/_autodocs/examples.md Configures setup and teardown logic independently for each function being benchmarked. This allows for fine-grained control over the environment for each test. ```elixir Benchee.run( %{ "with_cache" => { fn repo -> cached_query(repo) end, before_scenario: fn _ -> {:ok, repo} = Repo.start_link() QueryCache.enable() repo end, before_each: fn repo -> QueryCache.clear() repo end, after_scenario: fn repo -> QueryCache.disable() Repo.close(repo) end }, "without_cache" => { fn repo -> uncached_query(repo) end, before_scenario: fn _ -> {:ok, repo} = Repo.start_link() repo end, after_scenario: fn repo -> Repo.close(repo) end } } ) ``` -------------------------------- ### Running Benchee from Erlang Shell Source: https://github.com/bencheeorg/benchee/blob/main/README.md Example of invoking Benchee benchmarks from an Erlang shell after setting up the project with rebar3. This demonstrates calling the `:benchee` interface with a specific function and configuration. ```erlang 1> benchee:run(#{myFunc => fun() -> lists:sort([8, 2, 3, 4, 2, 1, 3, 4, 9, 10, 11, 12, 13, 20, 1000, -4, -5]) end}, [{warmup, 0}, {time, 2}]). ``` -------------------------------- ### Global before_scenario Hook Source: https://github.com/bencheeorg/benchee/blob/main/_autodocs/hooks-and-lifecycle.md Use a global before_scenario hook to perform setup before a scenario begins. It receives the scenario's input and can return a modified input or additional data for subsequent hooks. ```Elixir Benchee.run( %{ "test" => fn input -> process(input) end }, before_scenario: fn input -> # Setup: start resources, initialize state resource = start_expensive_resource() # Can return modified input or tuple with additional data {input, resource} end ) ``` -------------------------------- ### Benchmark Elixir with Module Invocation Source: https://github.com/bencheeorg/benchee/blob/main/README.md This example shows how to invoke Benchee from within a module to mitigate a known Elixir performance issue. It structures the benchmark code within a dedicated module, ensuring optimized function execution. ```elixir defmodule Compiled do def comprehension(list) do for x <- list, rem(x, 2) == 1, do: x + 1 end end defmodule MyBenchmark do def run do list = Enum.to_list(1..10_000) Benchee.run(%{ "module (optimized)" => fn -> Compiled.comprehension(list) end, "top_level (non-optimized)" => fn -> for x <- list, rem(x, 2) == 1, do: x + 1 end }) end end MyBenchmark.run() ``` -------------------------------- ### Initialize Benchmark Suite Source: https://github.com/bencheeorg/benchee/blob/main/_autodocs/api-reference.md Use `Benchee.init/1` to create a new benchmark suite with default and configured settings. ```Elixir Benchee.init([]) ``` -------------------------------- ### Benchee.init/1 Source: https://github.com/bencheeorg/benchee/blob/main/_autodocs/api-reference.md Initialize a benchmark suite with the provided configuration. This function sets up a new suite with default and specified settings. ```APIDOC ## Benchee.init/1 ### Description Initialize a benchmark suite with the provided configuration. This function sets up a new suite with default and specified settings. ### Function Signature ```elixir @spec init(keyword) :: Benchee.Suite.t() def init(config \ []) ``` ### Parameters #### Positional Parameters - **config** (keyword) - Optional - Configuration options. Delegates to `Benchee.Configuration.init/1`. Defaults to `[]`. ### Returns - `Benchee.Suite.t()` - A new suite with default and configured settings. ``` -------------------------------- ### Add Benchee to Mix Dependencies Source: https://github.com/bencheeorg/benchee/blob/main/README.md Add Benchee as a development dependency in your `mix.exs` file. Ensure you have Elixir 1.7+ installed. ```elixir defp deps do [ {:benchee, "~> 1.0", only: :dev} ] end ``` -------------------------------- ### Local before_scenario Hook Source: https://github.com/bencheeorg/benchee/blob/main/_autodocs/hooks-and-lifecycle.md Define a before_scenario hook locally for a specific scenario. This is useful for scenario-specific setup that should not affect other scenarios. ```Elixir Benchee.run( %{ "test" => { fn {input, resource} -> process(input, resource) end, before_scenario: fn input -> {input, start_resource()} end } } ) ``` -------------------------------- ### Add Escript Dependency to Mix Project Source: https://github.com/bencheeorg/benchee/blob/main/test/fixtures/escript/README.md Add the Escript package to your project's dependencies in mix.exs to install it via Hex. ```elixir def deps do [ {:escript, "~> 0.1.0"} ] end ``` -------------------------------- ### Configure Percentiles for Statistics Source: https://github.com/bencheeorg/benchee/blob/main/_autodocs/configuration.md Specifies which percentiles to calculate in the statistics. For example, the 99th percentile indicates a value below which 99% of measurements fall. ```elixir Benchee.run(jobs, percentiles: [50, 90, 95, 99, 99.9]) ``` -------------------------------- ### Benchee Initialization Phase Source: https://github.com/bencheeorg/benchee/blob/main/_autodocs/module-architecture.md This snippet details the first step of the Benchee execution pipeline, where the `Benchee.run/2` function calls `Benchee.init/1` to normalize and validate configuration, returning a `Benchee.Suite` struct with the applied settings. ```elixir Benchee.run(jobs, config) └── Benchee.init/1 [lib/benchee/configuration.ex] └── Normalize config keyword list to Configuration struct └── Apply defaults └── Validate inputs └── Return: Benchee.Suite with configuration ``` -------------------------------- ### Benchee.Configuration.init/1 Source: https://github.com/bencheeorg/benchee/blob/main/_autodocs/api-reference.md Initializes and normalizes benchmark configuration from a keyword list. It applies default values, performs validation, and converts the input into a `Benchee.Configuration` struct. ```APIDOC ## Benchee.Configuration.init/1 ### Description Initializes and normalizes configuration from a keyword list. Applies defaults and performs validation. ### Method `init(config \ [])` ### Parameters #### Keyword List Parameters - **config** (keyword) - Optional - The configuration options to apply. ### Returns - `Benchee.Suite.t()` - The initialized benchmark suite configuration. ``` -------------------------------- ### Local before_each Hook Source: https://github.com/bencheeorg/benchee/blob/main/_autodocs/hooks-and-lifecycle.md Configure a before_each hook locally for a specific scenario to run setup code before each iteration of that scenario's function. ```Elixir Benchee.run( %{ "test" => { fn input -> process(input) end, before_each: fn input -> fetch_from_db(input) end } } ) ``` -------------------------------- ### Benchee System Type Definition Source: https://github.com/bencheeorg/benchee/blob/main/_autodocs/types.md Captures system information at the start of a benchmark, including Elixir/Erlang versions, CPU details, and available memory. ```elixir @type Benchee.System.t :: %Benchee.System{ elixir: String.t(), erlang: String.t(), jit_enabled?: boolean(), num_cores: pos_integer(), os: :macOS | :Windows | :FreeBSD | :Solaris | :Linux, cpu_speed: String.t(), available_memory: String.t() } ``` -------------------------------- ### Run Benchmarks with Simple Interface Source: https://github.com/bencheeorg/benchee/blob/main/_autodocs/api-reference.md Use `Benchee.run/2` for straightforward benchmarking. Configure measurement time and memory time using keyword options. ```Elixir list = Enum.to_list(1..10_000) map_fun = fn i -> [i, i * i] end Benchee.run( %{ "flat_map" => fn -> Enum.flat_map(list, map_fun) end, "map.flatten" => fn -> list |> Enum.map(map_fun) |> List.flatten() end }, time: 10, memory_time: 2 ) ``` -------------------------------- ### Benchee Scenario Display Name Function Source: https://github.com/bencheeorg/benchee/blob/main/_autodocs/api-reference.md Retrieves the display name for a scenario, including the tag if present. Use this to get a formatted name for reporting. ```Elixir @spec display_name(Scenario.t()) :: String.t() def display_name(scenario) ``` -------------------------------- ### Add System Information to Suite Source: https://github.com/bencheeorg/benchee/blob/main/_autodocs/api-reference.md Use `Benchee.system/1` to augment a benchmark suite with operating system, CPU, memory, and version details. ```Elixir suite |> Benchee.system() ``` -------------------------------- ### Hook Execution Order Explanation Source: https://github.com/bencheeorg/benchee/blob/main/README.md Illustrates the sequence of hook calls for a given Benchee configuration, showing how local hooks execute closer to the benchmarking function than global hooks. ```text suite_set_up # starting with the foo scenario global_before_scenario(1) local_before_scenario(2) # as before_scenario incremented it global_before_each(3) local_before_each(4) foo(5) # let's say foo(5) returns 6 local_after_each(6) global_after_each(6) global_before_each(3) local_before_each(4) foo(5) # let's say foo(5) returns 6 local_after_each(6) global_after_each(6) # ... this block is repeated as many times as benchee has time local_after_scenario(3) global_after_scenario(3) # now it's time for the bar scenario, it has no hooks specified for itself # so only the global hooks are run global_before_scenario(1) global_before_each(2) bar(3) # let's say foo(3) returns 4 global_after_each(4) global_before_each(2) bar(3) # let's say foo(3) returns 4 global_after_each(4) # ... this block is repeated as many times as benchee has time global_after_scenario(2) suite_tear_down ``` -------------------------------- ### Run Benchee with Configuration Options Source: https://github.com/bencheeorg/benchee/blob/main/README.md This snippet shows how to pass configuration options to Benchee.run/2 using a keyword list. The `print: [benchmarking: false]` option disables the printing of benchmarking progress messages. ```elixir Benchee.run(%{"some function" => fn -> magic end}, print: [benchmarking: false]) ``` -------------------------------- ### Benchee.run/2 Source: https://github.com/bencheeorg/benchee/blob/main/_autodocs/api-reference.md Main entry point for running benchmarks with a simple, concise interface. It orchestrates the entire benchmarking process from initialization to output. ```APIDOC ## Benchee.run/2 ### Description Main entry point for running benchmarks with a simple, concise interface. It orchestrates the entire benchmarking process from initialization to output. ### Function Signature ```elixir @spec run(map, keyword) :: Benchee.Suite.t() def run(jobs, config \ []) ``` ### Parameters #### Positional Parameters - **jobs** (map) - Required - Map where keys are benchmark names (String or atom) and values are functions to benchmark. Functions should take no arguments if no inputs are specified, or one argument if inputs are configured. - **config** (keyword) - Optional - Configuration options controlling warmup time, measurement duration, inputs, formatters, hooks, and more. See Benchee.Configuration for full options. Defaults to `[]`. ### Returns - `Benchee.Suite.t()` - The complete benchmark suite with system information, configuration, and all scenarios with measured data and statistics. ### Example ```elixir list = Enum.to_list(1..10_000) map_fun = fn i -> [i, i * i] end Benchee.run( %{ "flat_map" => fn -> Enum.flat_map(list, map_fun) end, "map.flatten" => fn -> list |> Enum.map(map_fun) |> List.flatten() end }, time: 10, memory_time: 2 ) ``` ``` -------------------------------- ### Erlang Integration with Rebar3 Source: https://github.com/bencheeorg/benchee/blob/main/README.md Configuration for rebar.config to integrate Benchee into an Erlang project using the rebar3_elixir_compile plugin. This setup allows running Elixir benchmarks from an Erlang environment. ```erlang deps, [ {benchee, {elixir, "benchee", "0.9.0"}} ]}. {plugins, [ { rebar3_elixir_compile, ".*", {git, "https://github.com/barrel-db/rebar3_elixir_compile.git", {branch, "main"}}} ]}. {provider_hooks, [ {pre, [{compile, {ex, compile}}]}, {pre, [{release, {ex, compile}}]} ]}. {elixir_opts, [ {env, dev} ] }. ``` -------------------------------- ### Benchee.Benchmark.benchmark/3 Source: https://github.com/bencheeorg/benchee/blob/main/_autodocs/api-reference.md Adds a benchmarking scenario to the suite. This function creates one scenario per input or a single scenario if no inputs are configured, allowing for flexible benchmark setup. ```APIDOC ## Benchee.Benchmark.benchmark/3 Add a benchmarking scenario to the suite. ```elixir @spec benchmark(Suite.t(), Suite.key(), Scenario.to_benchmark()) :: Suite.t() def benchmark(suite, name, to_benchmark) ``` Creates one scenario per input (or a single scenario if no inputs are configured). Functions can be plain functions or tuples with local hook options. ``` -------------------------------- ### Benchee.system/1 Source: https://github.com/bencheeorg/benchee/blob/main/_autodocs/api-reference.md Add system information to the suite, including OS, CPU, memory, and Elixir/Erlang versions. This provides context for benchmark results. ```APIDOC ## Benchee.system/1 ### Description Add system information to the suite (OS, CPU, memory, Elixir/Erlang versions). This provides context for benchmark results. ### Function Signature ```elixir @spec system(Suite.t()) :: Suite.t() def system(suite) ``` ### Parameters #### Positional Parameters - **suite** (Suite.t()) - Required - The benchmark suite to augment with system information. ### Returns - `Benchee.Suite.t()` - Suite with `system` field populated. ``` -------------------------------- ### Define Before Scenario Hook Source: https://github.com/bencheeorg/benchee/blob/main/_autodocs/configuration.md Implement a global hook executed before each scenario. This hook receives the input and should return a value passed to `before_each`, used for setup that shouldn't be timed. ```elixir Benchee.run( jobs, before_scenario: fn input -> resource = start_expensive_setup() {input, resource} end ) ``` -------------------------------- ### Benchee Configuration Initialization Source: https://github.com/bencheeorg/benchee/blob/main/_autodocs/api-reference.md Initializes and normalizes the Benchee configuration from a keyword list. Defaults are applied, and validation is performed. ```Elixir def init(config \\ []) do config end ``` -------------------------------- ### Benchee.System Struct Source: https://github.com/bencheeorg/benchee/blob/main/_autodocs/api-reference.md Represents system information captured at the start of a benchmark run. It includes details about the Elixir and Erlang versions, JIT status, CPU information, and available memory. ```APIDOC ## Benchee.System System information captured at benchmark start. ### Struct Fields - `elixir` (String) - Elixir version. - `erlang` (String) - Erlang/OTP version. - `jit_enabled?` (boolean) - Whether JIT compilation is enabled (OTP 24+). - `num_cores` (positive integer) - Number of available CPU cores. - `os` (:Linux, :macOS, :Windows, :FreeBSD, :Solaris) - Operating system. - `cpu_speed` (String) - CPU model and speed information. - `available_memory` (String) - Total available RAM formatted (e.g., "16 GB"). ``` -------------------------------- ### Run Benchee Benchmark with Parallel Execution (4 Processes) Source: https://github.com/bencheeorg/benchee/wiki/Parallel-Benchmarking Execute a Benchee benchmark with 4 parallel processes. Performance continues to degrade, and deviation increases. ```bash tobi@happy ~/github/benchee $ mix run samples/run_parallel.exs # parallel 4 Benchmarking flat_map... Benchmarking map.flatten... Name ips average deviation median map.flatten 954.88 1047.25μs (±34.02%) 957.00μs flat_map 452.38 2210.55μs (±21.05%) 1914.00μs Comparison: map.flatten 954.88 flat_map 452.38 - 2.11x slower ``` -------------------------------- ### after_each Hook Example Source: https://github.com/bencheeorg/benchee/blob/main/README.md Demonstrates an after_each hook for validating results or performing cleanup after each benchmarking function invocation. This hook receives the result of the benchmarking function, and its return value is discarded. ```elixir Benchee.run( %{ "bar" => fn input -> bar(input) end }, inputs: %{ "input 1" => 1}, after_each: fn result -> assert result == 42 end ) ``` -------------------------------- ### Measure Memory Consumption Source: https://github.com/bencheeorg/benchee/blob/main/README.md This example shows how to enable memory consumption measurement in Benchee. It configures Benchee to run for a specified duration for memory measurement, while other metrics are not explicitly configured in this snippet. ```elixir Benchee.run( %{ "something_great" => fn -> cool_stuff end }, memory_time: 2 ) ``` -------------------------------- ### Run Benchee Benchmark with Parallel Execution (12 Processes) Source: https://github.com/bencheeorg/benchee/wiki/Parallel-Benchmarking Execute a Benchee benchmark with 12 parallel processes. This demonstrates significant performance degradation and is counter-productive for typical benchmarking. ```bash tobi@happy ~/github/benchee $ mix run samples/run_parallel.exs # parallel 12 Benchmarking flat_map... Benchmarking map.flatten... Name ips average deviation median map.flatten 296.63 3371.18μs (±57.60%) 2827.00μs flat_map 186.96 5348.74μs (±42.14%) 5769.50μs Comparison: map.flatten 296.63 flat_map 186.96 - 1.59x slower ``` -------------------------------- ### Comprehensive Benchee Configuration Source: https://github.com/bencheeorg/benchee/blob/main/_autodocs/configuration.md A comprehensive configuration for Benchee, specifying warmup and measurement times, memory and reduction measurement times, parallel execution, input variations, output saving and loading, custom formatters, percentiles, pre-check, profiler, and a title. ```elixir Benchee.run( %{ "flat_map" => fn list -> Enum.flat_map(list, fn x -> [x, x*2] end) end, "map+flatten" => fn list -> list |> Enum.map(fn x -> [x, x*2] end) |> List.flatten() end }, warmup: 3, time: 10, memory_time: 2, reduction_time: 2, parallel: 2, inputs: %{ "small" => Enum.to_list(1..100), "large" => Enum.to_list(1..10_000) }, save: [path: "results.benchee", tag: "v1"], load: ["baseline.benchee"], formatters: [ {Benchee.Formatters.Console, extended_statistics: true}, {BencheeHtml, file: "results.html"} ], percentiles: [50, 90, 99], pre_check: :all_same, profile_after: {:fprof, [sort: :own]}, title: "List Processing Benchmarks" ) ``` -------------------------------- ### Configure Benchee Run Options Source: https://github.com/bencheeorg/benchee/blob/main/_autodocs/README.md Set various parameters for a Benchee benchmark run, such as warmup time, measurement duration, parallel execution, and output formatting. ```elixir Benchee.run(jobs, warmup: 2, # Seconds before measuring time: 5, # Seconds to measure memory_time: 0, # Seconds for memory measurement (0 = disabled) parallel: 1, # Parallel processes inputs: nil, # Input values (%{name => value}) formatters: [Console], # Output formatters print: %{ benchmarking: true, # Print progress configuration: true, # Print config summary fast_warning: true # Warn for too-fast functions } ) ``` -------------------------------- ### Run Benchee with Console Formatter and Extended Statistics Source: https://github.com/bencheeorg/benchee/blob/main/README.md Configure the Console formatter to display extended statistics, including minimum, maximum, sample size, and mode. This provides a more detailed view of benchmark results. ```elixir list = Enum.to_list(1..10_000) map_fun = fn i -> [i, i * i] end Benchee.run( %{ "flat_map" => fn -> Enum.flat_map(list, map_fun) end, "map.flatten" => fn -> list |> Enum.map(map_fun) |> List.flatten() end }, time: 10, formatters: [{Benchee.Formatters.Console, extended_statistics: true}] ) ``` -------------------------------- ### Benchee.System Struct Definition Source: https://github.com/bencheeorg/benchee/blob/main/_autodocs/api-reference.md Defines the structure for capturing system information at the start of a benchmark. Includes Elixir and Erlang versions, JIT status, CPU core count, OS, and memory/CPU speed. ```elixir defstruct [ :elixir, :erlang, :jit_enabled?, :num_cores, :os, :available_memory, :cpu_speed ] ``` -------------------------------- ### after_scenario Hook Example Source: https://github.com/bencheeorg/benchee/blob/main/README.md Illustrates using an after_scenario hook to clean up resources or perform actions after a scenario completes. The hook receives the scenario's return value as input but its return value is discarded. ```elixir Benchee.run( %{ "bar" => fn -> bar() end }, after_scenario: fn _input -> bust_my_cache() end ) ``` -------------------------------- ### Run Dialyzer for Type Checking Source: https://github.com/bencheeorg/benchee/blob/main/README.md Initiate Dialyzer for static analysis. The first run may be slow; consider building PLTs beforehand. ```shell mix dialyzer ``` ```shell mix dialyzer --plt ``` -------------------------------- ### Benchee Benchmark Execution Flow (Warmup) Source: https://github.com/bencheeorg/benchee/blob/main/_autodocs/module-architecture.md Illustrates the sequence of operations during the warmup phase of a Benchee benchmark. This phase prepares the system by running functions repeatedly without recording timing data. ```text └── Benchee.collect/1 [lib/benchee/benchmark.ex] └── Benchee.Benchmark.Runner.run/4 [lib/benchee/benchmark/runner.ex] └── For each scenario: └── before_scenario hook └── Warmup iterations (time: warmup seconds) │ ├── Global before_each hook │ ├── Local before_each hook │ ├── Execute function (timing ignored) │ ├── Local after_each hook │ └── Global after_each hook └── (Measurements from warmup discarded) ``` -------------------------------- ### Run Benchee with HTML formatter Source: https://github.com/bencheeorg/benchee/blob/main/_autodocs/formatters-and-output.md Configure Benchee to use the BencheeHtml formatter, specifying an output file for the generated HTML report. ```elixir Benchee.run(jobs, formatters: [ {BencheeHtml, file: "results.html"} ]) ``` -------------------------------- ### Resource Lifecycle Hook Pattern Source: https://github.com/bencheeorg/benchee/blob/main/_autodocs/hooks-and-lifecycle.md Demonstrates managing external resources like database connections using `before_scenario`, `after_scenario`, `before_each`, and `after_each` hooks. It shows starting/stopping a pool per scenario and acquiring/returning connections per iteration. ```elixir Benchee.run( %{ "operation" => fn conn -> execute(conn) end }, before_scenario: fn _input -> # Start connection pool once per scenario {:ok, conn} = start_connection_pool() conn end, after_scenario: fn conn -> # Stop connection pool once per scenario stop_connection_pool(conn) end, before_each: fn conn -> # Get connection from pool before each call conn_from_pool = get_connection(conn) conn_from_pool end, after_each: fn conn -> # Return connection to pool after each call return_connection(conn) end ) ``` -------------------------------- ### Hook Input/Output Flow Diagram Source: https://github.com/bencheeorg/benchee/blob/main/_autodocs/hooks-and-lifecycle.md Visualizes the data flow through Benchee's lifecycle hooks. Shows how input is transformed and passed between global and local before hooks, and how results from the main function are handled by after hooks. ```text Input (from configuration) ↓ [Global before_scenario] ← receives input ↓ [Local before_scenario] ← receives global's output ↓ (chained value) [Warmup/Measurement iterations] │ ├─ [Global before_each] ← receives scenario_input │ ↓ │ [Local before_each] ← receives global's output │ ↓ (chained value) │ [Function call] ← receives local's output │ ↓ (function result) │ [Local after_each] ← receives function result │ [Global after_each] ← receives function result │ [Local after_scenario] ← receives scenario_input ↓ [Global after_scenario] ← receives scenario_input ``` -------------------------------- ### Run Benchee Benchmark with Parallel Execution (3 Processes) Source: https://github.com/bencheeorg/benchee/wiki/Parallel-Benchmarking Execute a Benchee benchmark with 3 parallel processes. Note the further degradation in performance and increased deviation. ```bash tobi@happy ~/github/benchee $ mix run samples/run_parallel.exs # parallel 3 Benchmarking flat_map... Benchmarking map.flatten... Name ips average deviation median map.flatten 1012.77 987.39μs (±29.53%) 913.00μs flat_map 513.44 1947.63μs (±6.91%) 1943.50μs Comparison: map.flatten 1012.77 flat_map 513.44 - 1.97x slower ``` -------------------------------- ### Run Tests Source: https://github.com/bencheeorg/benchee/blob/main/README.md Execute all project tests using this command. ```shell mix test ``` -------------------------------- ### Run Benchee Benchmark with Parallel Execution (2 Processes) Source: https://github.com/bencheeorg/benchee/wiki/Parallel-Benchmarking Execute a Benchee benchmark with 2 parallel processes. Observe the performance impact and increased deviation compared to sequential execution. ```bash tobi@happy ~/github/benchee $ mix run samples/run_parallel.exs # parallel 2 Benchmarking flat_map... Benchmarking map.flatten... Name ips average deviation median map.flatten 1230.53 812.66μs (±19.86%) 761.00μs flat_map 713.82 1400.92μs (±5.63%) 1416.00μs Comparison: map.flatten 1230.53 flat_map 713.82 - 1.72x slower ``` -------------------------------- ### Basic Input Configuration (List of Tuples Format) Source: https://github.com/bencheeorg/benchee/blob/main/_autodocs/inputs-and-data.md Configure benchmark inputs using a list of tuples, suitable for older Elixir versions. This format achieves the same result as the map format for input specification. ```elixir Benchee.run( jobs, inputs: [ {"small", [1, 2, 3]}, {"medium", Enum.to_list(1..1000)}, {"large", Enum.to_list(1..100_000)} ] ) ``` -------------------------------- ### Run Benchee with JSON formatter Source: https://github.com/bencheeorg/benchee/blob/main/_autodocs/formatters-and-output.md Integrate the BencheeJson formatter to export benchmark results in JSON format to a specified file. ```elixir Benchee.run(jobs, formatters: [ {BencheeJson, file: "results.json"} ]) ``` -------------------------------- ### Minimal Benchee Configuration Source: https://github.com/bencheeorg/benchee/blob/main/_autodocs/configuration.md A minimal configuration for Benchee. Uses default values for warmup time, measurement time, output format, and disables memory/reduction measurement. ```elixir Benchee.run(%{ "operation" => fn -> 1 + 1 end }) ``` -------------------------------- ### Basic Input Configuration (Map Format) Source: https://github.com/bencheeorg/benchee/blob/main/_autodocs/inputs-and-data.md Configure benchmark inputs using a map, which is recommended for Elixir 1.11+. This allows benchmarking the same function with different data sets. ```elixir Benchee.run( %{ "operation" => fn data -> process(data) end }, inputs: %{ "small" => [1, 2, 3], "medium" => Enum.to_list(1..1000), "large" => Enum.to_list(1..100_000) } ) ``` -------------------------------- ### Run Benchmark with Optimization and Load Source: https://github.com/bencheeorg/benchee/blob/main/_autodocs/examples.md Run a benchmark for an optimized implementation, specifying input data, duration, and loading previous results. ```elixir Benchee.run( %{ "implementation" => fn list -> sort_list_optimized(list) end }, inputs: %{"list" => Enum.to_list(1..10_000)}, time: 5, load: "baseline.benchee", # Load previous results save: [path: "optimized.benchee", tag: "after_optimization"] ) ``` -------------------------------- ### Run Benchee with Profiling Enabled Source: https://github.com/bencheeorg/benchee/blob/main/README.md Enable profiling after a Benchee run using the `profile_after` option. This will execute each scenario once and use the default `:eprof` profiler to identify performance bottlenecks. ```elixir list = Enum.to_list(1..10_000) map_fun = fn i -> [i, i * i] end Benchee.run( %{ "flat_map" => fn -> Enum.flat_map(list, map_fun) end, "map.flatten" => fn -> list |> Enum.map(map_fun) |> List.flatten() end }, profile_after: true ) ``` -------------------------------- ### Run Benchee with Markdown formatter Source: https://github.com/bencheeorg/benchee/blob/main/_autodocs/formatters-and-output.md Add the BencheeMarkdown formatter to generate benchmark reports in Markdown format, saving them to a specified file. ```elixir Benchee.run(jobs, formatters: [ {BencheeMarkdown, file: "results.md"} ]) ``` -------------------------------- ### Load and Report Saved Benchmarks Source: https://github.com/bencheeorg/benchee/blob/main/_autodocs/api-reference.md Use `Benchee.report/1` to load and format previously saved benchmarks without running new ones. The `:load` option is mandatory. ```Elixir Benchee.report( load: ["benchmark-*.benchee"], formatters: [Benchee.Formatters.Console] ) ``` -------------------------------- ### Local Hook Configuration Source: https://github.com/bencheeorg/benchee/blob/main/_autodocs/hooks-and-lifecycle.md Demonstrates configuring a local hook for a specific function within a Benchee run using a tuple. ```elixir {function, before_each: hook} ``` -------------------------------- ### Combine Results from Multiple Runs Source: https://github.com/bencheeorg/benchee/blob/main/_autodocs/formatters-and-output.md Demonstrates how to save benchmark results from one run and then load and compare them with results from a subsequent run. This is useful for comparing performance changes. ```elixir # Run 1 - save results Benchee.run(jobs_a, save: [path: "run1.benchee", tag: "baseline"]) # Run 2 - load and compare Benchee.run( jobs_b, load: ["run1.benchee"], save: [path: "run2.benchee", tag: "optimized"] ) ``` -------------------------------- ### Scenario Display Name Properties Source: https://github.com/bencheeorg/benchee/blob/main/_autodocs/inputs-and-data.md Demonstrates how to access and understand the `name` and `display_name` properties of a Benchee scenario. `display_name` can include tags for clarity. ```elixir # Function: Benchee.Scenario.display_name/1 scenario.name # "foo" (if new) scenario.display_name # "foo" (same) scenario.name # "foo" (if loaded from file with tag) scenario.tag # "baseline" scenario.display_name # "foo (baseline)" ``` -------------------------------- ### Benchee.Formatter Behaviour: write/2 Callback Source: https://github.com/bencheeorg/benchee/blob/main/_autodocs/api-reference.md Callback for performing I/O with formatted benchmark data. This function handles writing to the console, files, or network and returns :ok or an error tuple. ```elixir @callback write(any, options) :: :ok | {:error, String.t()} ``` -------------------------------- ### Minimal Output Configuration Source: https://github.com/bencheeorg/benchee/blob/main/_autodocs/configuration.md Configures Benchee to print minimal output by disabling benchmarking, configuration, and fast warning messages. ```elixir Benchee.run(jobs, print: %{ benchmarking: false, configuration: false, fast_warning: false }) ``` -------------------------------- ### Realistic Input Data Source: https://github.com/bencheeorg/benchee/blob/main/_autodocs/examples.md This pattern demonstrates how to use realistic data for benchmarking. Ensure your input data mirrors production characteristics for more accurate results. ```elixir Benchee.run( %{ "operation" => fn data -> process(data) end }, inputs: %{ "production_like" => load_production_like_data() } ) ```