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
raxol examples memory_dsl_example.ex
Raw

examples/memory_dsl_example.ex

defmodule Raxol.Examples.MemoryDSLExample do
@moduledoc """
Example demonstrating the enhanced Memory DSL with assertions.
This module showcases how to use the Memory DSL for comprehensive
memory testing with specific assertions and thresholds.
Usage:
iex> Raxol.Examples.MemoryDSLExample.run_memory_benchmarks()
"""
use Raxol.Benchmark.MemoryDSL
# =============================================================================
# Memory Benchmark Definition
# =============================================================================
memory_benchmark "Terminal Operations Memory Test" do
# Configure memory benchmark behavior
memory_config(
time: 2,
memory_time: 1,
warmup: 0.5,
# 15% regression threshold
regression_threshold: 0.15
)
# Define memory test scenarios
scenario("small_terminal", fn ->
create_small_terminal_buffer()
end)
scenario("large_terminal", fn ->
create_large_terminal_buffer()
end)
scenario("buffer_operations", fn ->
perform_buffer_operations()
end)
scenario("ansi_processing", fn ->
process_ansi_sequences()
end)
scenario("memory_stress_test", fn ->
perform_memory_stress_test()
end)
# Memory Assertions - Phase 3 Enhanced DSL Features
# Peak memory usage assertions
# 5MB
assert_memory_peak(:small_terminal, less_than: 5_000_000)
# 150MB
assert_memory_peak(:large_terminal, less_than: 150_000_000)
# 50MB
assert_memory_peak(:buffer_operations, less_than: 50_000_000)
# 10MB
assert_memory_peak(:ansi_processing, less_than: 10_000_000)
# 200MB
assert_memory_peak(:memory_stress_test, less_than: 200_000_000)
# Sustained memory usage assertions (75th percentile)
# 3MB
assert_memory_sustained(:small_terminal, less_than: 3_000_000)
# 100MB
assert_memory_sustained(:large_terminal, less_than: 100_000_000)
# 30MB
assert_memory_sustained(:buffer_operations, less_than: 30_000_000)
# 8MB
assert_memory_sustained(:ansi_processing, less_than: 8_000_000)
# Garbage collection pressure assertions
# Max 5 GC collections
assert_gc_pressure(:small_terminal, less_than: 5)
# Max 20 GC collections
assert_gc_pressure(:large_terminal, less_than: 20)
# Max 15 GC collections
assert_gc_pressure(:buffer_operations, less_than: 15)
# Max 50 GC collections
assert_gc_pressure(:memory_stress_test, less_than: 50)
# Memory efficiency assertions (higher is better)
# 80% efficiency
assert_memory_efficiency(:small_terminal, greater_than: 0.8)
# 60% efficiency
assert_memory_efficiency(:large_terminal, greater_than: 0.6)
# 70% efficiency
assert_memory_efficiency(:buffer_operations, greater_than: 0.7)
# 90% efficiency
assert_memory_efficiency(:ansi_processing, greater_than: 0.9)
# Memory regression detection compared to baseline
# 10% regression tolerance
assert_no_memory_regression(baseline: "v1.4.0", threshold: 0.1)
end
# =============================================================================
# Scenario Implementations
# =============================================================================
defp create_small_terminal_buffer do
# Create a standard 80x24 terminal buffer
for row <- 1..24 do
for col <- 1..80 do
%{
char: random_char(),
fg: random_color(),
bg: :black,
style: %{
bold: :rand.uniform() > 0.8,
italic: :rand.uniform() > 0.9,
underline: :rand.uniform() > 0.95
}
}
end
end
end
defp create_large_terminal_buffer do
# Create a large 1000x1000 terminal buffer
for row <- 1..1000 do
for col <- 1..1000 do
%{
char: random_char(),
fg: random_color(),
bg: random_bg_color(),
style: %{
bold: :rand.uniform() > 0.7,
italic: :rand.uniform() > 0.8,
underline: :rand.uniform() > 0.9
}
}
end
end
end
defp perform_buffer_operations do
# Create buffer and perform various operations
buffer = create_small_terminal_buffer()
# Simulate buffer modifications
updated_buffer =
Enum.map(buffer, fn row ->
Enum.map(row, fn cell ->
if :rand.uniform() > 0.5 do
%{cell | char: "X", fg: :red}
else
cell
end
end)
end)
# Simulate scrolling operations
scrolled_buffer =
case updated_buffer do
[] ->
[]
[_first | rest] ->
empty_row =
for _col <- 1..80 do
%{char: " ", fg: :white, bg: :black, style: %{}}
end
rest ++ [empty_row]
end
# Simulate copy operations
copied_sections =
for _i <- 1..10 do
section_start = :rand.uniform(20)
section_end = section_start + 3
Enum.slice(scrolled_buffer, section_start..section_end)
end
{scrolled_buffer, copied_sections}
end
defp process_ansi_sequences do
# Process various ANSI escape sequences
sequences = [
# Clear screen
"\e[2J",
# Cursor to home
"\e[1;1H",
# Red colored text
"\e[31mRed text\e[0m",
# Bold text
"\e[1mBold text\e[0m",
# Underlined text
"\e[4mUnderlined\e[0m",
# Inverted colors
"\e[7mInverted\e[0m",
# Enable alternative buffer
"\e[?1049h",
# Disable alternative buffer
"\e[?1049l",
# 256-color mode
"\e[38;5;196mBright red\e[0m",
# 256-color background
"\e[48;5;21mBlue bg\e[0m",
# True color mode
"\e[38;2;255;128;0mRGB\e[0m"
]
# Process each sequence multiple times
processed =
for sequence <- sequences do
for _iteration <- 1..100 do
parse_ansi_sequence(sequence)
end
end
# Create result buffer showing processed sequences
result_buffer =
for {sequence, index} <- Enum.with_index(sequences) do
%{
sequence: sequence,
processed_count: 100,
result: Enum.at(processed, index),
memory_impact: byte_size(sequence) * 100
}
end
result_buffer
end
defp perform_memory_stress_test do
# Create memory pressure scenario
large_data_chunks =
for chunk_id <- 1..1000 do
chunk_data =
for _byte <- 1..1024 do
:rand.uniform(255)
end
%{
id: chunk_id,
data: chunk_data,
metadata: %{
created_at: System.system_time(:microsecond),
size: length(chunk_data),
checksum: Enum.sum(chunk_data)
}
}
end
# Perform operations that should trigger garbage collection
processed_chunks =
large_data_chunks
|> Enum.chunk_every(100)
|> Enum.map(fn chunk_group ->
# Process each group
processed =
Enum.map(chunk_group, fn chunk ->
# Transform data to trigger allocations
doubled_data = Enum.map(chunk.data, &(&1 * 2))
%{chunk | data: doubled_data}
end)
# Calculate group statistics
total_size = Enum.sum_by(processed, fn chunk -> length(chunk.data) end)
avg_checksum =
Enum.sum_by(processed, fn chunk -> chunk.metadata.checksum end) /
length(processed)
%{
group_size: total_size,
avg_checksum: avg_checksum,
chunks: processed
}
end)
# Return summary to avoid keeping all data in memory
%{
total_groups: length(processed_chunks),
total_memory_processed: Enum.sum_by(processed_chunks, & &1.group_size),
avg_group_size:
Enum.sum_by(processed_chunks, & &1.group_size) /
length(processed_chunks)
}
end
# =============================================================================
# Helper Functions
# =============================================================================
defp random_char do
chars =
~c"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_+ "
Enum.random(chars)
end
defp random_color do
colors = [:black, :red, :green, :yellow, :blue, :magenta, :cyan, :white]
Enum.random(colors)
end
defp random_bg_color do
if :rand.uniform() > 0.8 do
random_color()
else
:black
end
end
defp parse_ansi_sequence(sequence) do
# Simple ANSI sequence parser simulation
cond do
String.contains?(sequence, "[2J") ->
{:clear_screen, byte_size(sequence)}
String.contains?(sequence, "[1;1H") ->
{:cursor_home, byte_size(sequence)}
String.contains?(sequence, "m") ->
{:color_command, extract_color_info(sequence), byte_size(sequence)}
String.contains?(sequence, "?1049") ->
if String.contains?(sequence, "h") do
{:alt_buffer_enable, byte_size(sequence)}
else
{:alt_buffer_disable, byte_size(sequence)}
end
true ->
{:unknown_sequence, sequence, byte_size(sequence)}
end
end
defp extract_color_info(sequence) do
cond do
String.contains?(sequence, "31m") ->
{:fg_color, :red}
String.contains?(sequence, "32m") ->
{:fg_color, :green}
String.contains?(sequence, "38;5;") ->
{:fg_color_256, extract_256_color(sequence)}
String.contains?(sequence, "38;2;") ->
{:fg_color_rgb, extract_rgb_color(sequence)}
String.contains?(sequence, "48;5;") ->
{:bg_color_256, extract_256_color(sequence)}
String.contains?(sequence, "48;2;") ->
{:bg_color_rgb, extract_rgb_color(sequence)}
String.contains?(sequence, "1m") ->
{:style, :bold}
String.contains?(sequence, "4m") ->
{:style, :underline}
String.contains?(sequence, "7m") ->
{:style, :reverse}
String.contains?(sequence, "0m") ->
{:reset, :all}
true ->
{:unknown_color, sequence}
end
end
defp extract_256_color(sequence) do
# Extract 256-color value (simplified)
case Regex.run(~r/38;5;(\d+)/, sequence) do
[_, color_str] -> String.to_integer(color_str)
_ -> 0
end
end
defp extract_rgb_color(sequence) do
# Extract RGB color values (simplified)
case Regex.run(~r/38;2;(\d+);(\d+);(\d+)/, sequence) do
[_, r_str, g_str, b_str] ->
{String.to_integer(r_str), String.to_integer(g_str),
String.to_integer(b_str)}
_ ->
{0, 0, 0}
end
end
# =============================================================================
# Public API
# =============================================================================
@doc """
Run the memory benchmark example with assertions.
Returns a detailed report including assertion results and recommendations.
"""
def run_example do
IO.puts("Running Memory DSL Example...")
IO.puts("This demonstrates Phase 3 advanced memory analysis capabilities.")
IO.puts("")
case run_memory_benchmarks() do
{:ok, report} ->
print_example_report(report)
{:ok, report}
{:error, error} ->
IO.puts("Error running memory benchmarks: #{inspect(error)}")
{:error, error}
end
end
defp print_example_report(report) do
IO.puts("=== Memory DSL Example Report ===")
IO.puts("")
# Print summary
summary = report.summary
IO.puts("Summary:")
IO.puts(" Total scenarios: #{summary.total_scenarios}")
IO.puts(" Total assertions: #{summary.total_assertions}")
IO.puts(" Passing assertions: #{summary.passing_assertions}")
IO.puts(" Failing assertions: #{summary.failing_assertions}")
IO.puts(" Success rate: #{Float.round(summary.success_rate * 100, 1)}%")
IO.puts("")
# Print assertion results
IO.puts("Assertion Results:")
for {{assertion_type, scenario}, result} <- report.assertion_results do
status =
case result do
{:ok, _} -> "PASS"
{:error, _} -> "FAIL"
end
message =
case result do
{:ok, msg} -> msg
{:error, msg} -> msg
end
IO.puts(" [#{status}] #{assertion_type} (#{scenario}): #{message}")
end
IO.puts("")
# Print memory analysis
analysis = report.memory_analysis
IO.puts("Memory Analysis:")
IO.puts(" Peak memory: #{format_bytes(analysis.peak_memory)}")
IO.puts(" Sustained memory: #{format_bytes(analysis.sustained_memory)}")
IO.puts(" GC collections: #{analysis.gc_collections}")
IO.puts(
" Fragmentation ratio: #{Float.round(analysis.fragmentation_ratio, 3)}"
)
IO.puts(" Efficiency score: #{Float.round(analysis.efficiency_score, 3)}")
IO.puts(" Regression detected: #{analysis.regression_detected}")
IO.puts("")
# Print recommendations
if length(report.recommendations) > 0 do
IO.puts("Optimization Recommendations:")
for {recommendation, index} <- Enum.with_index(report.recommendations, 1) do
IO.puts(" #{index}. #{recommendation}")
end
else
IO.puts("Optimization Recommendations: None - memory usage is optimal!")
end
end
defp format_bytes(bytes) when bytes >= 1_000_000_000 do
"#{Float.round(bytes / 1_000_000_000, 2)} GB"
end
defp format_bytes(bytes) when bytes >= 1_000_000 do
"#{Float.round(bytes / 1_000_000, 2)} MB"
end
defp format_bytes(bytes) when bytes >= 1_000 do
"#{Float.round(bytes / 1_000, 2)} KB"
end
defp format_bytes(bytes) do
"#{bytes} B"
end
end