Packages
Multi-surface application runtime for Elixir. One TEA module renders to terminal, browser (LiveView), SSH, and MCP (agents). 30+ widgets, flexbox + CSS grid, AI agent runtime, distributed swarm with CRDTs, time-travel debugging, session recording, sandboxed REPL, and agentic commerce.
Current section
Files
Jump to
Current section
Files
lib/mix/tasks/raxol.bench.ex
defmodule Mix.Tasks.Raxol.Bench do
@moduledoc """
Enhanced benchmarking task for Raxol terminal emulator.
Provides comprehensive performance benchmarking with multiple
output formats, regression detection, and dashboard generation.
## Usage
mix raxol.bench # Run all benchmarks
mix raxol.bench parser # Run parser benchmarks only
mix raxol.bench terminal # Run terminal component benchmarks
mix raxol.bench rendering # Run rendering benchmarks only
mix raxol.bench memory # Run memory benchmarks only
mix raxol.bench dashboard # Generate dashboard from latest results
## Options
* `--quick` - Quick benchmark run with reduced time
* `--compare` - Compare with previous benchmark results
* `--dashboard` - Generate full performance dashboard with charts
* `--regression` - Check for performance regressions (5% threshold)
* `--help` - Show detailed help
## Examples
# Quick performance check
mix raxol.bench --quick
# Full benchmarks with regression detection
mix raxol.bench --regression
# Parser benchmarks with dashboard generation
mix raxol.bench parser --dashboard
# Compare current run with previous results
mix raxol.bench --compare
## Output
Results are saved to `bench/output/enhanced/` with:
- HTML reports for interactive viewing
- JSON data for programmatic analysis
- Performance dashboard with charts
- Regression analysis reports
- Baseline metrics for future comparison
"""
use Mix.Task
require Logger
@shortdoc "Run enhanced performance benchmarks"
@compile {:no_warn_undefined, Raxol.Bench.Dashboard}
@compile {:no_warn_undefined, Raxol.Bench.TestData}
@compile {:no_warn_undefined, Benchee}
alias Raxol.Bench.{Dashboard, TestData}
@switches [
quick: :boolean,
compare: :boolean,
dashboard: :boolean,
regression: :boolean,
help: :boolean,
format: :string,
output: :string,
suite: :string
]
def run(args) do
{opts, args, _} = OptionParser.parse(args, switches: @switches)
if opts[:help] do
print_help()
else
Mix.Task.run("app.start")
execute_benchmark(args, opts)
end
end
defp execute_benchmark(args, opts) do
# Handle --suite flag as primary routing for new framework suites
if suite = opts[:suite] do
run_framework_suite(suite, opts)
else
execute_benchmark_by_arg(args, opts)
end
end
@framework_suites ~w(render latency memory_widgets startup comparison)
defp execute_benchmark_by_arg([], opts), do: run_all_benchmarks(opts)
defp execute_benchmark_by_arg([suite], opts) when suite in @framework_suites,
do: run_framework_suite(suite, opts)
defp execute_benchmark_by_arg(["parser"], opts), do: run_parser_only(opts)
defp execute_benchmark_by_arg(["terminal"], opts), do: run_terminal_only(opts)
defp execute_benchmark_by_arg(["rendering"], opts),
do: run_rendering_only(opts)
defp execute_benchmark_by_arg(["memory"], opts), do: run_memory_only(opts)
defp execute_benchmark_by_arg(["dashboard"], opts),
do: run_dashboard_only(opts)
defp execute_benchmark_by_arg([benchmark], _opts) do
Mix.shell().error("Unknown benchmark: #{benchmark}")
print_help()
end
defp run_all_benchmarks(opts) do
Mix.shell().info("Running Raxol Performance Benchmarks...")
if opts[:quick] do
run_quick_benchmarks()
else
run_comprehensive_benchmarks(opts)
end
end
defp run_quick_benchmarks do
Mix.shell().info("Quick benchmark mode (reduced time)")
benchmark_config = [
time: 1,
memory_time: 0.5,
warmup: 0.2,
formatters: [Benchee.Formatters.Console]
]
_ = run_parser_benchmarks(benchmark_config, quick: true)
_ = run_terminal_benchmarks(benchmark_config, quick: true)
end
defp run_comprehensive_benchmarks(opts) do
Mix.shell().info("Running comprehensive benchmarks...")
File.mkdir_p!("bench/output/enhanced")
File.mkdir_p!("bench/output/enhanced/json")
File.mkdir_p!("bench/output/enhanced/html")
timestamp = DateTime.utc_now() |> DateTime.to_iso8601()
results =
%{}
|> Map.put(:parser, run_parser_benchmarks(standard_config(timestamp)))
|> Map.put(:terminal, run_terminal_benchmarks(standard_config(timestamp)))
|> Map.put(
:rendering,
run_rendering_benchmarks(standard_config(timestamp))
)
|> Map.put(:memory, run_memory_benchmarks(standard_config(timestamp)))
|> Map.put(
:concurrent,
run_concurrent_benchmarks(standard_config(timestamp))
)
if opts[:regression], do: Dashboard.check_for_regressions(results)
if opts[:dashboard],
do: Dashboard.generate_enhanced_dashboard(results, timestamp)
if opts[:compare], do: Dashboard.run_comparison_analysis(results, timestamp)
Dashboard.print_results_summary(results, timestamp)
end
defp run_parser_only(opts) do
Mix.shell().info("Running Parser Benchmarks Only...")
config = if opts[:quick], do: quick_config(), else: standard_config()
run_parser_benchmarks(config)
end
defp run_terminal_only(opts) do
Mix.shell().info("Running Terminal Component Benchmarks Only...")
config = if opts[:quick], do: quick_config(), else: standard_config()
run_terminal_benchmarks(config)
end
defp run_rendering_only(opts) do
Mix.shell().info("Running Rendering Benchmarks Only...")
config = if opts[:quick], do: quick_config(), else: standard_config()
run_rendering_benchmarks(config)
end
defp run_memory_only(opts) do
Mix.shell().info("Running Memory Benchmarks Only...")
config = if opts[:quick], do: quick_config(), else: standard_config()
run_memory_benchmarks(config)
end
defp run_dashboard_only(_opts) do
Mix.shell().info("Generating Performance Dashboard...")
timestamp = DateTime.utc_now() |> DateTime.to_iso8601()
results = Dashboard.load_latest_results() || %{}
Dashboard.generate_enhanced_dashboard(results, timestamp)
Mix.shell().info(
"Dashboard generated at bench/output/enhanced/dashboard.html"
)
end
defp run_parser_benchmarks(config, opts \\ []) do
alias Raxol.Terminal.ANSI.StateMachine
alias Raxol.Terminal.ANSI.Utils.AnsiParser
alias Raxol.Terminal.Emulator
_emulator = Emulator.new(80, 24)
scenarios =
if opts[:quick] do
%{
"plain_text" => "Hello, World!",
"ansi_basic" => "\e[31mRed Text\e[0m",
"ansi_complex" => "\e[1;4;31mBold Red Underlined\e[0m"
}
else
TestData.full_parser_scenarios()
end
jobs =
Enum.into(scenarios, %{}, fn {name, content} ->
{name,
fn ->
state_machine = StateMachine.new()
AnsiParser.parse(state_machine, content)
end}
end)
Benchee.run(jobs, config)
end
defp run_terminal_benchmarks(config, opts \\ []) do
jobs =
if opts[:quick],
do: TestData.terminal_quick_jobs(),
else: TestData.terminal_comprehensive_jobs()
Benchee.run(jobs, config)
end
defp run_rendering_benchmarks(config) do
alias Raxol.UI.Rendering.RenderBatcher
{_small_buffer, medium_buffer, large_buffer} =
TestData.create_test_buffers()
jobs = %{
"render_with_damage_tracking" => fn ->
RenderBatcher.batch_render(medium_buffer,
changed_regions: [{0, 0, 10, 10}]
)
end,
"render_full_redraw" => fn ->
RenderBatcher.batch_render(large_buffer, full_redraw: true)
end
}
Benchee.run(jobs, config)
end
defp run_memory_benchmarks(config) do
Benchee.run(memory_benchmark_jobs(), config)
end
defp memory_benchmark_jobs do
alias Raxol.Terminal.ANSI.StateMachine
alias Raxol.Terminal.ANSI.Utils.AnsiParser
alias Raxol.Terminal.Cursor
alias Raxol.Terminal.Emulator
alias Raxol.Terminal.ScreenBufferAdapter, as: ScreenBuffer
%{
"memory_emulator_80x24" => fn -> Emulator.new(80, 24) end,
"memory_emulator_200x50" => fn -> Emulator.new(200, 50) end,
"memory_parser_session" => fn ->
emulator = Emulator.new(80, 24)
state_machine = StateMachine.new()
content = TestData.generate_test_content()
_ = AnsiParser.parse(state_machine, content)
{emulator, state_machine}
end,
"memory_buffer_operations" => fn ->
buffer = ScreenBuffer.new(100, 30)
_cursor = Cursor.new()
Enum.reduce(1..50, buffer, fn i, acc ->
ScreenBuffer.write_string(acc, 0, rem(i, 30), "Test line #{i}")
end)
end,
"memory_large_buffer_allocation" => fn -> ScreenBuffer.new(500, 500) end,
"memory_plugin_system" => fn ->
alias Raxol.Plugins.Manager
{:ok, manager} = Manager.new()
Manager.list_plugins(manager)
end
}
end
defp run_concurrent_benchmarks(config) do
jobs = %{
"concurrent_emulator_creation" => &bench_emulator_creation/0,
"concurrent_buffer_writes" => &bench_buffer_writes/0,
"concurrent_event_dispatch" => &bench_event_dispatch/0,
"concurrent_plugin_operations" => &bench_plugin_operations/0
}
Benchee.run(jobs, config)
end
defp bench_emulator_creation do
alias Raxol.Terminal.Emulator
1..10
|> Enum.map(fn _ -> Task.async(fn -> Emulator.new(80, 24) end) end)
|> Task.await_many()
end
defp bench_buffer_writes do
alias Raxol.Terminal.ScreenBufferAdapter, as: ScreenBuffer
buffer = ScreenBuffer.new(80, 24)
1..10
|> Enum.map(fn i ->
Task.async(fn -> ScreenBuffer.write_char(buffer, i, i, "X") end)
end)
|> Task.await_many()
end
defp bench_event_dispatch do
alias Raxol.Core.Events.EventManager
case EventManager.start_link(name: EventManager) do
{:ok, _pid} -> :ok
{:error, {:already_started, _pid}} -> :ok
end
1..20
|> Enum.map(fn i ->
Task.async(fn -> EventManager.dispatch(:test_event, %{id: i}) end)
end)
|> Task.await_many()
end
defp bench_plugin_operations do
alias Raxol.Plugins.Manager
{:ok, manager} = Manager.new()
1..5
|> Enum.map(fn _ ->
Task.async(fn ->
_plugins = Manager.list_plugins(manager)
Manager.get_plugin_state(manager, "test_plugin")
end)
end)
|> Task.await_many()
end
defp standard_config(timestamp \\ nil) do
ts = timestamp || DateTime.utc_now() |> DateTime.to_iso8601()
[
time: 5,
memory_time: 2,
warmup: 1,
formatters: [
Benchee.Formatters.Console,
{Benchee.Formatters.JSON,
file: "bench/output/enhanced/json/benchmark_#{ts}.json"},
{Benchee.Formatters.HTML,
file: "bench/output/enhanced/html/benchmark_#{ts}.html"}
],
save: [path: "bench/output/enhanced/benchee_#{ts}.benchee"],
load: "bench/output/enhanced/*.benchee"
]
end
defp quick_config do
[
time: 1,
memory_time: 0.5,
warmup: 0.2,
formatters: [Benchee.Formatters.Console]
]
end
# -- Framework-level suites --
@compile {:no_warn_undefined, Raxol.Benchmark.Formatter}
defp run_framework_suite(suite, opts) do
quick = opts[:quick] || false
case resolve_suite(suite, quick) do
{:run, jobs, config} ->
prev_level = Logger.level()
if suite == "startup", do: Logger.configure(level: :error)
result = Benchee.run(jobs, config)
if suite == "startup", do: Logger.configure(level: prev_level)
maybe_write_output(result, suite, opts)
:skip ->
handle_non_benchee_suite(suite, opts)
end
end
defp resolve_suite(suite, quick) do
config = if quick, do: quick_config(), else: framework_config()
case suite do
"render" ->
{:run, Raxol.Benchmark.Suites.RenderThroughput.jobs(quick: quick),
config}
"latency" ->
{:run, Raxol.Benchmark.Suites.InputLatency.jobs(quick: quick), config}
"memory_widgets" ->
config = Keyword.put(config, :memory_time, 2)
{:run, Raxol.Benchmark.Suites.WidgetMemory.jobs(quick: quick), config}
"startup" ->
{:run, Raxol.Benchmark.Suites.Startup.jobs(quick: quick), config}
_ ->
:skip
end
end
defp handle_non_benchee_suite("comparison", _opts),
do: print_comparison_table()
defp handle_non_benchee_suite("all", opts), do: run_all_framework_suites(opts)
defp handle_non_benchee_suite(other, _opts) do
Mix.shell().error("Unknown suite: #{other}")
end
defp run_all_framework_suites(opts) do
for suite <- ~w(render latency memory_widgets startup) do
Mix.shell().info("\n--- Suite: #{suite} ---")
run_framework_suite(suite, opts)
end
print_comparison_table()
end
defp print_comparison_table do
Mix.shell().info("\n=== COMPETITOR COMPARISON (static reference) ===")
shell = Mix.shell()
Raxol.Benchmark.Suites.Comparison.comparison_table()
|> Enum.each(fn line -> shell.info(line) end)
Mix.shell().info(
"\nNote: Competitor numbers are published estimates. Raxol numbers are measured above."
)
end
defp maybe_write_output(result, suite, opts) do
case opts[:format] do
nil ->
:ok
format ->
write_formatted_output(format, opts[:output], %{suite => result})
end
end
defp write_formatted_output("json", path, data) when is_binary(path) do
content = Raxol.Benchmark.Formatter.json(data, include_system: true)
:ok = Raxol.Benchmark.Formatter.write(content, path)
Mix.shell().info("JSON results written to #{path}")
end
defp write_formatted_output("markdown", path, data) when is_binary(path) do
content = Raxol.Benchmark.Formatter.markdown(data)
:ok = Raxol.Benchmark.Formatter.write(content, path)
Mix.shell().info("Markdown results written to #{path}")
end
defp write_formatted_output("json", nil, data),
do: IO.puts(Raxol.Benchmark.Formatter.json(data))
defp write_formatted_output("markdown", nil, data),
do: IO.puts(Raxol.Benchmark.Formatter.markdown(data))
defp write_formatted_output(_format, _path, _data), do: :ok
defp framework_config do
[
time: 3,
memory_time: 1,
warmup: 0.5,
formatters: [Benchee.Formatters.Console]
]
end
defp print_help do
Mix.shell().info("""
Raxol Enhanced Benchmarking Tool
Usage:
mix raxol.bench # Run all benchmarks
mix raxol.bench render # Framework render throughput
mix raxol.bench latency # Framework input latency
mix raxol.bench memory_widgets # Widget memory profiling
mix raxol.bench startup # Lifecycle startup timing
mix raxol.bench comparison # Show competitor reference data
mix raxol.bench parser # Legacy: parser benchmarks
mix raxol.bench terminal # Legacy: terminal benchmarks
mix raxol.bench rendering # Legacy: rendering benchmarks
mix raxol.bench memory # Legacy: memory benchmarks
mix raxol.bench dashboard # Generate dashboard from latest results
Options:
--suite NAME # Run a specific suite (render, latency, etc.)
--quick # Quick benchmark run (reduced time)
--format FORMAT # Output format: json, markdown
--output PATH # Write formatted output to file
--compare # Compare with previous results
--dashboard # Generate full performance dashboard
--regression # Check for performance regressions (5% threshold)
--help # Show this help
Examples:
mix raxol.bench --quick # Quick performance check
mix raxol.bench render # Render throughput only
mix raxol.bench --suite render --format markdown --output BENCHMARKS.md
mix raxol.bench parser --dashboard # Parser benchmarks + dashboard
mix raxol.bench --regression # Full benchmarks with regression detection
Output:
Results are saved to bench/output/enhanced/ with:
* HTML reports for interactive viewing
* JSON data for programmatic analysis
* Performance dashboard with charts
* Regression analysis reports
* Baseline metrics for comparison
""")
end
end