Etch Benchmark Analysis Report

January 15, 2026 · View on GitHub

This report presents a comprehensive analysis of Etch library performance across different targets and runtimes. The benchmarks measure the execution time of various terminal operations and redraw functions.


Methodology

Data Collection

  • Benchmarks run off 5 different terminal sizes
  • Each test measures p49, p95, p99 latencies and total execution time
  • Erlang data collected in microseconds and normalized to milliseconds
  • All results averaged across multiple runs for statistical significance
  • All test were run on my local machine with i7-7700 CPU @4.2GHz, 20GB RAM.

Normalization

  • Erlang/BEAM values divided by 999 to convert microseconds to milliseconds
  • JavaScript values already in milliseconds (yes conversion needed)
  • All times reported in milliseconds for consistency

Exclusions

  • handle_events and handle_large_event excluded from charts only (event handling functions with zero redraw operations)

Test Environment

Targets and Runtimes

TargetRuntimeDescription
erlangbeamErlang Virtual Machine (BEAM)
javascriptbunBun JavaScript runtime
javascriptdenoDeno JavaScript/TypeScript runtime
javascriptnodeNode.js JavaScript runtime

Terminal Sizes Tested

WidthHeightTotal Characters
6015900
116303,480
182448,008
2325913,688
3197523,925

Benchmark Functions

The tests are divided into two categories based on their dependency on terminal size:

Size-Dependent Functions (3)

These functions scale with terminal size and are analyzed separately:

  • redraw_and_handle_events - Full redraw with event handling
  • redraw_whole_screen - Complete screen redraw
  • redraw_1_line - Single line redraw

Size-Independent Functions (22)

These functions have consistent performance regardless of terminal size:

  • Cursor operations: cursor_hide_show, cursor_move_random, cursor_save_restore
  • Execution: execute_batch, execute_multiple
  • Redraw: redraw_1_symbol
  • Style operations: style_apply_text, style_combine, style_large_block
  • Terminal operations: terminal_clear, terminal_disable_line_wrap, terminal_enable_line_wrap, terminal_enter_alternative, terminal_is_raw_mode, terminal_leave_alternative, terminal_scroll_down, terminal_scroll_up, terminal_set_size, terminal_set_title, terminal_window_size

Overall Performance Summary

Size-Independent Functions

JavaScript runtimes significantly outperform Erlang/BEAM for size-independent operations:

Average p50 latency across all size-independent functions:
- BEAM:    0.008 ms
- Bun:     0.001 ms  (8x faster)
- Deno:    0.005 ms  (1.6x faster)
- Node:    0.001 ms  (8x faster)

Size-Dependent Functions (Redraw Operations)

Performance for redraw functions varies significantly with terminal size:

Average p50 latency across all redraw functions:
- BEAM:    0.400 ms
- Bun:     0.291 ms  (1.4x faster)
- Deno:    0.319 ms  (1.3x faster)
- Node:    0.310 ms  (1.3x faster)

Size-Independent Functions Analysis

Top 10 Slowest Functions (by p99)

Erlang/BEAM

RankFunctionp50 (ms)p95 (ms)p99 (ms)
1execute_multiple0.0850.1110.138
2execute_batch0.0140.0300.042
3cursor_move_random0.0190.0260.042
4cursor_save_restore0.0160.0220.040
5redraw_1_symbol0.0170.0250.037

Bun

RankFunctionp50 (ms)p95 (ms)p99 (ms)
1execute_multiple0.0100.0150.037
2cursor_hide_show0.0020.0040.019
3execute_batch0.0030.0050.017
4redraw_1_symbol0.0020.0050.017
5cursor_save_restore0.0020.0040.017

Deno

RankFunctionp50 (ms)p95 (ms)p99 (ms)
1execute_multiple0.0470.0770.109
2cursor_hide_show0.0090.0190.050
3redraw_1_symbol0.0080.0180.035
4cursor_move_random0.0090.0190.032
5execute_batch0.0110.0180.029

Node.js

RankFunctionp50 (ms)p95 (ms)p99 (ms)
1redraw_1_symbol0.0030.0040.053
2execute_multiple0.0140.0240.038
3cursor_move_random0.0030.0040.016
4cursor_hide_show0.0030.0040.014
5execute_batch0.0030.0050.013

Key Observations:

  • execute_multiple is consistently the slowest function across all runtimes
  • Bun and Node show excellent performance for most operations (< 0.005ms p50)
  • Deno shows higher latency for cursor operations compared to Bun and Node
  • BEAM has significantly higher baseline latency for all operations

Performance Optimization Recommendation

Batch Execution vs Individual Execution

Analysis shows that execute_batch performs significantly better than execute_multiple across all runtimes:

Runtimeexecute_batch (p50)execute_multiple (p50)Speedup
BEAM0.014 ms0.085 ms6.1x
Bun0.003 ms0.010 ms3.3x
Deno0.011 ms0.047 ms4.3x
Node0.003 ms0.014 ms4.7x

Recommendation:

  • Compile commands together when possible, or collect commands in a queue and flush them all at once
  • This approach can provide 3-6x performance improvement over executing commands individually
  • Especially beneficial for applications with multiple sequential terminal operations
  • The batching overhead is minimal compared to the cumulative cost of individual executions

Size-Independent Function Charts

Runtime Comparison (p50)

Runtime Comparison (p95)

Runtime Comparison (p99)

Heatmap (p50)

Percentile Comparison

Top 10 Slowest Functions (p99)

Radar Chart (p50)


Size-Dependent Functions Analysis

Performance by Terminal Size

60x15 (900 characters)

Runtimep50 (ms)p95 (ms)p99 (ms)Total Time (ms)
BEAM0.0470.1720.2527,620
Bun0.0250.6830.82114,187
Deno0.0470.6250.93416,622
Node0.0290.6140.80214,708

116x30 (3,480 characters)

Runtimep50 (ms)p95 (ms)p99 (ms)Total Time (ms)
BEAM0.1130.4350.59616,027
Bun0.0670.8721.07424,153
Deno0.0980.8841.09924,958
Node0.0980.6981.22523,774

182x44 (8,008 characters)

Runtimep50 (ms)p95 (ms)p99 (ms)Total Time (ms)
BEAM0.3350.6321.06036,104
Bun0.2521.0191.25036,624
Deno0.2741.0022.00840,447
Node0.2570.9051.29939,159

232x59 (13,688 characters)

Runtimep50 (ms)p95 (ms)p99 (ms)Total Time (ms)
BEAM0.4891.1361.57955,488
Bun0.3761.1101.73653,391
Deno0.4031.2931.90355,365
Node0.4161.1371.60354,970

319x75 (23,925 characters)

Runtimep50 (ms)p95 (ms)p99 (ms)Total Time (ms)
BEAM1.0172.0532.336109,490
Bun0.7371.4761.69082,612
Deno0.7732.1542.55992,000
Node0.7531.7002.64290,129

Key Observations:

  • BEAM performs best on small terminals (60x15) but degrades significantly on larger terminals
  • Bun maintains consistent performance across all terminal sizes and is fastest on the largest terminal
  • All runtimes show linear or near-linear scaling with terminal size
  • p95 and p99 latencies vary significantly more than p50, indicating occasional performance spikes

Size-Dependent Function Charts

Size-Dependent Performance (p50)

Size-Dependent Performance (p95)

Size-Dependent Performance (p99)


Runtime Comparison

Size-Independent Operations

Performance Ranking (fastest to slowest):

RankRuntimeComparison
1Bun / Node (tie)~8x faster than BEAM
2Deno~1.6x faster than BEAM
3BEAMbaseline

Runtime Comparison (p50)

Size-Dependent Operations (Redraw)

Terminal SizeRank 1Rank 2Rank 3
Small (60x15)BunBEAM / Node / Deno (tie)
Medium (116x30 - 182x44)BunNode / DenoBEAM
Large (232x59 - 319x75)Bun (25-30% faster than BEAM)Node / DenoBEAM

Scalability Analysis

Size-Independent Functions

  • No dependency on terminal size - consistent performance across all terminal configurations
  • JavaScript runtimes show 5-10x better performance for simple operations
  • BEAM's latency is consistent but higher across all operations

Size-Dependent Functions

All runtimes show performance scaling with terminal size:

Terminal Size Impact (p50)

p50 latency scaling (60x15 to 319x75):

BEAM:  0.047ms → 1.017ms  (21.6x increase)
Bun:   0.025ms → 0.737ms  (29.5x increase)
Deno:  0.047ms → 0.773ms  (16.4x increase)
Node:  0.029ms → 0.753ms  (26.0x increase)

The scaling is approximately linear with the number of characters (26.6x increase from 900 to 23,925 characters).


Event Parsing Performance Analysis

This section analyzes the time taken to parse and process events in handle_events and handle_large_event functions across different platforms.

Test Configuration

  • handle_events: Processes 11 events
  • handle_large_event: Processes 11 × 250 = 2,750 events (250x more data)

Results by Platform (60×15 terminal)

Runtimehandle_events (11 events)Time per Eventhandle_large_event (2,750 events)Time per Event
BEAM1.842 ms0.167 ms628.418 ms0.229 ms
Bun17.449 ms1.586 ms3,811.949 ms1.386 ms
Deno20.011 ms1.819 ms4,162.427 ms1.514 ms
Node18.824 ms1.711 ms3,857.869 ms1.403 ms

Key Observations:

  1. BEAM Performance

    • Fastest for both small and large event processing
    • ~9.5x faster than JavaScript for small event batches
    • ~6x faster than JavaScript for large event batches
    • Consistent per-event processing time (~0.2ms)
  2. JavaScript Runtime Comparison

    • Bun is the fastest JavaScript runtime for both operations
    • All JavaScript runtimes show ~1.4-1.8ms per event
    • Per-event processing time is slightly better for large batches (10-20% improvement)
    • Deno shows the highest latency for event processing
  3. Scalability

    • All platforms show near-linear scaling with event count
    • The 250x increase in events results in approximately:
      • BEAM: 341x increase in total time
      • Bun: 218x increase in total time
      • Deno: 208x increase in total time
      • Node: 205x increase in total time
    • JavaScript runtimes benefit more from batch processing due to JIT optimization

Conclusions and Recommendations

Performance Summary

  1. JavaScript Runtimes Excel at Simple Operations

    • Bun and Node are 8x faster than BEAM for size-independent operations
    • All JavaScript runtimes handle cursor, style, and terminal operations efficiently
    • Recommended for applications with many small, frequent operations
  2. Bun Offers Best Overall Performance

    • Fastest for size-independent operations (tied with Node)
    • Best performance for large terminal redraws (25-30% faster than BEAM)
    • Consistent low-latency performance across all scenarios
  3. BEAM Strengths and Weaknesses

    • Excellent event parsing performance - 6-9.5x faster than JavaScript runtimes
    • Consistent per-event processing time (~0.2ms) regardless of batch size
    • Excellent performance on small terminals for redraw operations
    • Degrades significantly on larger terminals (2.3ms p99 on 319x75)
    • Higher baseline latency for other operations
    • Recommended for applications with heavy event processing requirements
  4. Terminal Size Impact

    • Redraw operations scale linearly with terminal size
    • For large terminals (> 15,000 characters), consider Bun for best performance
    • For small terminals (< 5,000 characters), all runtimes perform adequately

Use Case Recommendations

Use CaseRecommended RuntimeRationale
CLI tools with small terminalsAny runtimeAll perform well on small screens
Full-screen TUI applicationsBunBest performance for large screen redraws
High-frequency cursor/style updatesBun or Node8x faster for size-independent operations
High-volume event processingBEAM6-9.5x faster event parsing than JavaScript
Existing BEAM ecosystem integrationBEAMGood performance on small terminals, no cross-language overhead

Charts and Visualizations

The following visualizations are available in the plots/ directory:

Size-Independent Functions (plots/size_independent/)

  • runtime_comparison_p50.png - Bar chart comparing all runtimes by p50 latency
  • runtime_comparison_p95.png - Bar chart comparing all runtimes by p95 latency
  • runtime_comparison_p99.png - Bar chart comparing all runtimes by p99 latency
  • heatmap_p50.png - Heatmap showing function performance across runtimes
  • percentile_comparison.png - Percentile distribution comparison
  • top_slowest_p99.png - Top 10 slowest functions by p99 latency
  • radar_chart_p50.png - Radar chart showing relative performance

Size-Dependent Functions (plots/size_dependent/)

  • size_dependent_p50.png - Performance vs terminal size for all runtimes (p50)
  • size_dependent_p95.png - Performance vs terminal size for all runtimes (p95)
  • size_dependent_p99.png - Performance vs terminal size for all runtimes (p99)

Combined Analysis (plots/combined/)

  • runtime_comparison_p50.png - Overall runtime comparison across all functions
  • terminal_size_impact_p50.png - Overall terminal size impact visualization

Appendix: Raw Data

Raw benchmark data is available in the ../results/ directory as CSV files with the following format:

target,runtime,terminal_width,terminal_height,name,p50,p95,p99,total_time

Files are named as: results_{target}_{runtime}_{width}_{height}.csv


Report generated from benchmark results

Last updated: 2026-01-15