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/raxol/benchmark/runner.ex
defmodule Raxol.Benchmark.Runner do
@moduledoc """
Comprehensive benchmarking suite for Raxol performance testing.
Provides infrastructure for running, analyzing, and reporting performance benchmarks
across all major components of the system.
"""
@compile {:no_warn_undefined, Benchee}
alias Raxol.Benchmark.{Analyzer, Reporter, Storage}
alias Raxol.Core.Runtime.Log
@default_options %{
warmup: 2,
time: 5,
memory_time: 2,
parallel: 1,
formatters: [
Benchee.Formatters.Console,
{Benchee.Formatters.HTML, file: "bench/output/report.html"}
],
save: [path: "bench/snapshots/", tag: "latest"],
load: "bench/snapshots/*.benchee"
}
@type benchmark_suite :: %{
name: String.t(),
benchmarks: map(),
options: keyword()
}
@doc """
Runs a complete benchmark suite with all configured benchmarks.
"""
def run_all(opts \\ []) do
Log.info("Starting comprehensive benchmark suite...")
suites = [
terminal_benchmarks(),
rendering_benchmarks(),
plugin_benchmarks(),
buffer_benchmarks(),
component_benchmarks(),
security_benchmarks()
]
results =
Enum.map(suites, fn suite ->
Log.info("Running #{suite.name} benchmarks...")
run_suite(suite, opts)
end)
# Generate comprehensive report
_report = Reporter.generate_comprehensive_report(results)
# Analyze for regressions
regressions = Analyzer.check_regressions(results)
case Enum.any?(regressions) do
true ->
Log.warning("Performance regressions detected!")
Analyzer.report_regressions(regressions)
false ->
:ok
end
results
end
@doc """
Runs a specific benchmark suite.
"""
def run_suite(suite, opts \\ []) do
options =
@default_options
|> Map.merge(Enum.into(opts, %{}))
|> Map.to_list()
start_time = System.monotonic_time(:millisecond)
results =
Benchee.run(
suite.benchmarks,
options
)
end_time = System.monotonic_time(:millisecond)
duration = end_time - start_time
# Store results
Storage.save_results(suite.name, results, duration)
%{
suite_name: suite.name,
results: results,
duration: duration,
timestamp: DateTime.utc_now()
}
end
@doc """
Runs a single benchmark for quick testing.
"""
def run_single(name, benchmark_fn, opts \\ []) do
options =
@default_options
|> Map.merge(Enum.into(opts, %{}))
|> Map.to_list()
Benchee.run(
%{name => benchmark_fn},
options
)
end
@doc """
Profiles a specific operation with detailed analysis.
"""
def profile(name, operation, opts \\ []) do
Log.info("Profiling #{name}...")
# Run with profiling enabled
profile_opts =
Keyword.merge(opts,
profile_after: true,
print: [benchmarking: false, configuration: false]
)
results = run_single(name, operation, profile_opts)
# Analyze profile data
Analyzer.analyze_profile(name, results)
end
# Benchmark Suites
defp terminal_benchmarks do
%{
name: "Terminal Operations",
benchmarks: %{
"ANSI parsing" => fn input ->
Raxol.Terminal.Emulator.ANSIHandler.handle_ansi_sequences(input, %{})
end,
"Text writing" => fn ->
emulator = create_test_emulator()
Raxol.Terminal.Operations.TextOperations.write_text(
emulator,
"Hello, World!"
)
end
# Commented out due to dialyzer type issues - need to fix emulator struct
# "Cursor movement" => fn ->
# emulator = create_test_emulator()
# Raxol.Terminal.Operations.CursorOperations.move_cursor(
# emulator,
# 10,
# 10
# )
# end,
# "Screen clear" => fn ->
# emulator = create_test_emulator()
# Raxol.Terminal.Operations.ScreenOperations.clear_screen(emulator)
# end,
# "Resize operation" => fn ->
# emulator = create_test_emulator()
# Raxol.Terminal.Emulator.Dimensions.resize(emulator, 120, 40)
# end
},
options: [
inputs: %{
"small input" => String.duplicate("a", 100),
"medium input" => String.duplicate("test\n", 1000),
"large input" => String.duplicate("x", 10_000),
"ANSI heavy" => generate_ansi_heavy_input()
}
]
}
end
defp rendering_benchmarks do
%{
name: "Rendering Pipeline",
benchmarks: %{
"Simple scene" => fn scene ->
render_scene(scene)
end,
"Complex scene" => fn scene ->
render_scene(scene)
end,
"Animation frame" => fn ->
animate_frame(create_test_animation())
end,
"Layout calculation" => fn ->
calculate_layout(create_test_components())
end,
"Style processing" => fn ->
process_styles(create_test_styles())
end
},
options: [
inputs: %{
"minimal" => create_minimal_scene(),
"typical" => create_typical_scene(),
"complex" => create_complex_scene(),
"stress" => create_stress_scene()
}
]
}
end
defp plugin_benchmarks do
%{
name: "Plugin System",
benchmarks: %{
"Plugin load" => fn ->
Raxol.Core.Runtime.Plugins.PluginManager.load_plugin(TestPlugin)
end,
"Plugin message" => fn ->
send_plugin_message(:test_message, %{data: "test"})
end,
"Plugin lifecycle" => fn ->
lifecycle_test()
end,
"Plugin discovery" => fn ->
Raxol.Core.Runtime.Plugins.Discovery.discover_plugins("test/plugins")
end
},
options: []
}
end
defp buffer_benchmarks do
%{
name: "Buffer Operations",
benchmarks: %{
"Buffer write" => fn data ->
buffer = create_test_buffer()
Raxol.Terminal.Buffer.SafeManager.write(buffer, data)
end,
"Buffer read" => fn ->
buffer = create_test_buffer_with_data()
Raxol.Terminal.Buffer.SafeManager.read(buffer, 100)
end,
"Buffer scroll" => fn ->
buffer = create_test_buffer_with_data()
scroll_buffer(buffer, 10)
end,
"Buffer resize" => fn ->
buffer = create_test_buffer()
Raxol.Terminal.Buffer.SafeManager.resize(buffer, 120, 40)
end,
"Buffer search" => fn ->
buffer = create_test_buffer_with_data()
search_buffer(buffer, "pattern")
end
},
options: [
inputs: %{
"small" => String.duplicate("x", 100),
"medium" => String.duplicate("line\n", 1000),
"large" => String.duplicate("data", 10_000)
}
]
}
end
defp component_benchmarks do
%{
name: "UI Components",
benchmarks: %{
"Button render" => fn ->
render_component(:button, %{label: "Click me"})
end,
"Table render" => fn data ->
render_component(:table, %{rows: data})
end,
"Form validation" => fn ->
validate_form(create_test_form())
end,
"Layout flex" => fn ->
layout_flex_container(create_flex_items())
end,
"Theme apply" => fn ->
apply_theme(create_test_component(), :dark)
end
},
options: [
inputs: %{
"10 rows" => create_table_data(10),
"100 rows" => create_table_data(100),
"1000 rows" => create_table_data(1000)
}
]
}
end
defp security_benchmarks do
%{
name: "Security Operations",
benchmarks: %{
"Password hash" => fn ->
hash_password("test_password_123")
end
},
options: []
}
end
# Helper functions
defp create_test_emulator do
{w, h} = Raxol.Constants.default_terminal_dimensions()
Raxol.Terminal.Emulator.new(w, h)
end
defp create_test_buffer do
{:ok, buffer} = Raxol.Terminal.Buffer.SafeManager.start_link()
buffer
end
defp create_test_buffer_with_data do
buffer = create_test_buffer()
Enum.each(1..1000, fn i ->
Raxol.Terminal.Buffer.SafeManager.write(buffer, "Line #{i}\n")
end)
buffer
end
defp generate_ansi_heavy_input do
Enum.map(1..100, fn i ->
"\e[#{rem(i, 7) + 31}mColored text #{i}\e[0m\n"
end)
end
defp create_minimal_scene do
%{type: :text, content: "Hello"}
end
defp create_typical_scene do
%{
type: :container,
children:
Enum.map(1..10, fn i ->
%{type: :text, content: "Item #{i}"}
end)
}
end
defp create_complex_scene do
%{
type: :container,
style: %{background: :blue, padding: 2},
children:
Enum.map(1..100, fn i ->
%{
type: :box,
style: %{border: :single},
children: [
%{type: :text, content: "Complex item #{i}"}
]
}
end)
}
end
defp create_stress_scene do
# Create deeply nested scene for stress testing
Enum.reduce(1..50, %{type: :text, content: "Deep"}, fn _, acc ->
%{type: :container, children: [acc]}
end)
end
defp render_scene(_scene) do
# Placeholder for actual rendering
:ok
end
defp create_test_animation do
%{duration: 1000, frames: 60}
end
defp animate_frame(_animation) do
:ok
end
defp create_test_components do
Enum.map(1..20, fn i -> %{id: i, type: :box} end)
end
defp calculate_layout(_components) do
:ok
end
defp create_test_styles do
%{color: :red, background: :black, bold: true}
end
defp process_styles(_styles) do
:ok
end
defp send_plugin_message(_msg, _data) do
:ok
end
defp lifecycle_test do
:ok
end
defp scroll_buffer(_buffer, _lines) do
:ok
end
defp search_buffer(_buffer, _pattern) do
:ok
end
defp render_component(_type, _props) do
:ok
end
defp create_table_data(rows) do
Enum.map(1..rows, fn i ->
%{id: i, name: "Row #{i}", value: :rand.uniform(1000)}
end)
end
defp create_test_form do
%{fields: [:name, :email, :age]}
end
defp validate_form(_form) do
:ok
end
defp create_flex_items do
Enum.map(1..10, fn i -> %{flex: 1, id: i} end)
end
defp layout_flex_container(_items) do
:ok
end
defp create_test_component do
%{type: :button, label: "Test"}
end
defp apply_theme(_component, _theme) do
:ok
end
defp hash_password(_password) do
:crypto.hash(:sha256, "password")
end
defmodule TestPlugin do
@moduledoc "Test plugin for benchmark runner testing."
# Removed undefined @behaviour Raxol.Plugin
def init(_), do: {:ok, %{}}
def commands, do: []
def handle_event(_, state), do: {:ok, state}
def cleanup(_), do: :ok
end
end