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 lib raxol performance optimizer.ex
Raw

lib/raxol/performance/optimizer.ex

defmodule Raxol.Performance.Optimizer do
@moduledoc """
Refactored Performance optimization strategies with GenServer-based memoization.
This module provides the same optimization techniques as the original but uses
supervised state management instead of Process dictionary for memoization.
## Migration Notes
The memoize macro now uses Raxol.Performance.Memoization.Server instead
of Process dictionary, providing better fault tolerance and monitoring.
"""
import Raxol.Performance.Profiler
alias Raxol.Core.Runtime.Log
@doc """
Optimizes database queries to prevent N+1 problems.
## Examples
# Before optimization
users = Repo.all(User)
users_with_posts = Enum.map(users, fn user ->
%{user | posts: Repo.all(assoc(user, :posts))}
end)
# After optimization
users_with_posts = optimize_query do
User |> preload(:posts) |> Repo.all()
end
"""
defmacro optimize_query(do: query) do
quote do
# Profile the query first
profile :database_query, metadata: %{optimized: true} do
unquote(query)
end
end
end
@doc """
Implements caching for expensive operations.
## Options
- `:ttl` - Time to live in milliseconds (default: 60_000)
- `:key` - Cache key (required)
- `:refresh` - Whether to refresh cache on hit (default: false)
## Examples
cached :expensive_calculation, key: "calc_123", ttl: 300_000 do
perform_expensive_calculation(123)
end
"""
defmacro cached(operation, opts, do: block) do
quote do
Raxol.Performance.Optimizer.execute_with_cache(
unquote(operation),
unquote(opts),
fn -> unquote(block) end
)
end
end
@doc """
Implements lazy loading for large datasets.
## Examples
lazy_stream :large_file_reader do
File.stream!("large_file.txt")
|> Stream.map(&process_line/1)
end
"""
defmacro lazy_stream(operation, do: stream) do
quote do
profile unquote(operation), metadata: %{lazy: true} do
unquote(stream)
end
end
end
@doc """
Memoizes function results to avoid recomputation.
Now uses GenServer-based caching instead of Process dictionary.
## Examples
defmodule Calculator do
use Raxol.Performance.Optimizer
alias Raxol.Core.Runtime.Log
memoize expensive_calculation(n) do
# Complex calculation
factorial(n) * fibonacci(n)
end
end
"""
defmacro memoize({name, _, args} = _call, do: body) do
key = {name, args}
quote do
Raxol.Performance.Optimizer.ensure_memoization_server()
Raxol.Performance.Memoization.Server.get_or_compute(
unquote(key),
fn -> unquote(body) end
)
end
end
@doc """
Optimizes concurrent operations using Task.async_stream.
## Options
- `:max_concurrency` - Maximum concurrent tasks (default: System.schedulers_online())
- `:timeout` - Task timeout in milliseconds (default: 5000)
- `:ordered` - Maintain order of results (default: true)
## Examples
concurrent_map users, &send_email/1, max_concurrency: 10
"""
def concurrent_map(enumerable, fun, opts \\ []) do
max_concurrency =
Keyword.get(opts, :max_concurrency, System.schedulers_online())
timeout = Keyword.get(opts, :timeout, 5000)
ordered = Keyword.get(opts, :ordered, true)
profile :concurrent_operation, metadata: %{concurrency: max_concurrency} do
enumerable
|> Task.async_stream(fun,
max_concurrency: max_concurrency,
timeout: timeout,
ordered: ordered
)
|> Enum.map(fn
{:ok, result} -> result
{:exit, reason} -> {:error, reason}
end)
end
end
@doc """
Batches operations to reduce overhead.
## Examples
batch_process records, batch_size: 100 do |batch|
Repo.insert_all(Record, batch)
end
"""
def batch_process(enumerable, opts, fun) do
batch_size = Keyword.get(opts, :batch_size, 100)
enumerable
|> Stream.chunk_every(batch_size)
|> Enum.each(fn batch ->
profile :batch_operation, metadata: %{batch_size: length(batch)} do
fun.(batch)
end
end)
end
@doc """
Optimizes string concatenation for better performance.
## Examples
# Instead of multiple concatenations
result = str1 <> str2 <> str3 <> str4
# Use
result = string_builder([str1, str2, str3, str4])
"""
def string_builder(parts) when is_list(parts) do
profile :string_building, metadata: %{parts: length(parts)} do
IO.iodata_to_binary(parts)
end
end
@doc """
Implements circuit breaker pattern for external calls.
"""
def with_circuit_breaker(name, fun, opts \\ []) do
profile :circuit_breaker_call, metadata: %{circuit: name} do
Raxol.Core.ErrorRecovery.with_circuit_breaker(name, fun, opts)
end
end
@doc """
Optimizes list operations using appropriate data structures.
## Examples
# For frequent prepends, use lists
optimize_list_ops :prepend, initial_list do |list|
[new_item | list]
end
# For frequent lookups, convert to map
optimize_list_ops :lookup, list do |list|
Map.new(list, & {&1.id, &1})
end
"""
def optimize_list_ops(operation_type, data, transformer) do
profile :"list_ops_#{operation_type}",
metadata: %{size: length_or_size(data)} do
transformer.(data)
end
end
@doc """
Reduces memory usage by implementing streaming where possible.
## Examples
stream_process "large_file.csv" do |line|
parse_csv_line(line)
|> process_record()
end
"""
def stream_process(file_path, processor) do
profile :stream_processing, metadata: %{file: file_path} do
File.stream!(file_path)
|> Stream.map(processor)
|> Stream.run()
end
end
@doc """
Optimizes ETS table operations.
## Examples
ets_batch_insert(:my_table, records, batch_size: 1000)
"""
def ets_batch_insert(table, records, opts \\ []) do
batch_size = Keyword.get(opts, :batch_size, 1000)
records
|> Stream.chunk_every(batch_size)
|> Enum.each(fn batch ->
profile :ets_batch_insert,
metadata: %{table: table, size: length(batch)} do
:ets.insert(table, batch)
end
end)
end
@doc """
Implements connection pooling for external resources.
Uses poolboy if available, otherwise creates a simple connection pool using Registry.
## Examples
with_pooled_connection(:database, fn conn ->
MyRepo.query(conn, "SELECT * FROM users")
end)
"""
def with_pooled_connection(pool_name, fun) when is_function(fun, 1) do
profile :pooled_connection, metadata: %{pool: pool_name} do
case poolboy_available?() do
true ->
:poolboy.transaction(pool_name, fun)
false ->
# Fallback to simple connection management
case get_or_create_connection(pool_name) do
{:ok, conn} ->
execute_with_connection(pool_name, conn, fun)
{:error, reason} ->
Log.warning(
"Failed to get connection from pool #{pool_name}: #{inspect(reason)}"
)
{:error, :connection_unavailable}
end
end
end
end
def with_pooled_connection(pool_name, fun) when is_function(fun, 0) do
# For compatibility with zero-arity functions
profile :pooled_connection, metadata: %{pool: pool_name} do
case poolboy_available?() do
true -> :poolboy.transaction(pool_name, fn _conn -> fun.() end)
false -> fun.()
end
end
end
@doc """
Initializes a connection pool with the given configuration.
## Options
- `:size` - Pool size (default: 5)
- `:max_overflow` - Maximum overflow connections (default: 10)
- `:worker` - Worker module for connections
- `:worker_args` - Arguments for worker initialization
## Examples
init_connection_pool(:database,
size: 5,
max_overflow: 10,
worker: MyConnectionWorker,
worker_args: [host: "localhost", port: 5432]
)
"""
def init_connection_pool(pool_name, opts \\ []) do
case poolboy_available?() do
true -> init_poolboy_pool(pool_name, opts)
false -> init_simple_pool(pool_name, opts)
end
end
@doc """
Optimizes GenServer calls by batching.
## Examples
batch_genserver_calls(MyServer, messages, batch_size: 50)
"""
def batch_genserver_calls(server, messages, opts \\ []) do
batch_size = Keyword.get(opts, :batch_size, 50)
timeout = Keyword.get(opts, :timeout, 5000)
messages
|> Stream.chunk_every(batch_size)
|> Enum.map(fn batch ->
profile :batched_genserver_call,
metadata: %{server: server, batch_size: length(batch)} do
GenServer.call(server, {:batch, batch}, timeout)
end
end)
end
@doc """
Implements read-through cache pattern.
"""
def read_through_cache(key, loader, opts \\ []) do
ttl = Keyword.get(opts, :ttl, 60_000)
cache_name = Keyword.get(opts, :cache, :default_cache)
case lookup_cache(cache_name, key) do
{:ok, value} ->
profile :cache_hit, metadata: %{key: key} do
value
end
:miss ->
profile :cache_miss, metadata: %{key: key} do
value = loader.()
store_cache(cache_name, key, value, ttl)
value
end
end
end
@doc """
Optimizes recursive operations using tail recursion.
## Examples
# Convert recursive function to tail-recursive
def sum([]), do: 0
def sum([h | t]), do: h + sum(t)
# Becomes
tail_recursive_sum(list)
"""
def tail_recursive_sum(list), do: do_sum(list, 0)
defp do_sum([], acc), do: acc
defp do_sum([h | t], acc), do: do_sum(t, acc + h)
@doc """
Profiles and suggests algorithm improvements.
"""
def analyze_algorithm(name, implementations) do
results =
Enum.map(implementations, fn {impl_name, fun} ->
result =
benchmark(:"#{name}_#{impl_name}", iterations: 1000) do
fun.()
end
{impl_name, result}
end)
best =
results
|> Enum.min_by(fn {_, stats} -> stats.mean end)
|> elem(0)
%{
results: results,
recommendation: "Use #{best} implementation",
improvement_potential: calculate_improvement_potential(results)
}
end
def execute_with_cache(operation, opts, fun) do
key = Keyword.fetch!(opts, :key)
ttl = Keyword.get(opts, :ttl, 60_000)
refresh = Keyword.get(opts, :refresh, false)
cache_key = {operation, key}
case get_from_cache(cache_key) do
{:ok, value} when not refresh ->
value
_ ->
value =
profile operation, metadata: %{cache_miss: true} do
fun.()
end
put_in_cache(cache_key, value, ttl)
value
end
end
# Private functions
defp poolboy_available? do
Code.ensure_loaded?(:poolboy)
end
defp init_poolboy_pool(pool_name, opts) do
pool_config = [
name: {:local, pool_name},
worker_module:
Keyword.get(opts, :worker, Raxol.Performance.DefaultWorker),
size: Keyword.get(opts, :size, 5),
max_overflow: Keyword.get(opts, :max_overflow, 10)
]
worker_args = Keyword.get(opts, :worker_args, [])
case :poolboy.start_link(pool_config, worker_args) do
{:ok, _pid} -> {:ok, pool_name}
{:error, reason} -> {:error, reason}
end
end
defp init_simple_pool(pool_name, _opts) do
# Simple connection tracking using Registry
registry_name = :"#{pool_name}_connections"
case Registry.start_link(keys: :unique, name: registry_name) do
{:ok, _pid} ->
Log.info("Initialized simple connection pool: #{pool_name}")
{:ok, pool_name}
{:error, {:already_started, _pid}} ->
{:ok, pool_name}
{:error, reason} ->
{:error, reason}
end
end
defp get_or_create_connection(pool_name) do
registry_name = :"#{pool_name}_connections"
connection_id = "conn_#{System.unique_integer([:positive])}"
case Registry.register(registry_name, connection_id, %{
created_at: DateTime.utc_now()
}) do
{:ok, _pid} ->
# In a real implementation, this would create an actual connection
# For now, return a mock connection identifier
{:ok, %{id: connection_id, pool: pool_name}}
{:error, reason} ->
{:error, reason}
end
end
defp return_connection(pool_name, conn) do
registry_name = :"#{pool_name}_connections"
Registry.unregister(registry_name, conn.id)
:ok
end
defp get_from_cache(key) do
case :persistent_term.get({:cache, key}, :not_found) do
{:cached, value, expiry} ->
case expiry > System.system_time(:millisecond) do
true -> {:ok, value}
false -> :miss
end
_ ->
:miss
end
end
defp put_in_cache(key, value, ttl) do
expiry = System.system_time(:millisecond) + ttl
:persistent_term.put({:cache, key}, {:cached, value, expiry})
end
defp lookup_cache(_cache_name, key) do
get_from_cache(key)
end
defp store_cache(_cache_name, key, value, ttl) do
put_in_cache(key, value, ttl)
end
defp length_or_size(data) when is_list(data), do: length(data)
defp length_or_size(data) when is_map(data), do: map_size(data)
defp length_or_size(_), do: 0
defp calculate_improvement_potential(results) do
times = Enum.map(results, fn {_, stats} -> stats.mean end)
best = Enum.min(times)
worst = Enum.max(times)
%{
absolute: worst - best,
percentage: (worst - best) / worst * 100
}
end
defp execute_with_connection(pool_name, conn, fun) do
case Raxol.Core.ErrorHandling.safe_call(fn -> fun.(conn) end) do
{:ok, result} ->
return_connection(pool_name, conn)
result
{:error, reason} ->
return_connection(pool_name, conn)
{:error, reason}
end
end
end