A high-performance multi-language benchmarking framework with a custom DSL and full LSP support.
poly-bench lets you define benchmarks once and run them across multiple languages (Go, TypeScript, Rust) with unified output and comparison. It's designed for:
- Cross-language library comparisons — Compare your Go library against its TypeScript equivalent
- Performance regression testing — Track performance across languages over time
- Fair benchmarking — Same data, same iterations, unified measurement
# Install
curl -L https://raw.githubusercontent.com/evm-tooling/poly-bench/main/scripts/install.sh | bash
# Initialize a project
poly-bench init my-benchmarks
# Run benchmarks
poly-bench runuse std::charting
suite hash {
description: "Hash function benchmarks"
iterations: 5000
warmup: 100
baseline: "go"
setup go {
import "crypto/sha256"
helpers {
func hash(data []byte) []byte {
h := sha256.Sum256(data)
return h[:]
}
}
}
setup ts {
import { keccak256 } from 'viem'
}
fixture data {
hex: "68656c6c6f20776f726c64" # "hello world"
}
bench sha256 {
go: hash(data)
ts: keccak256(data_hex)
}
}
| Keyword | Description |
|---|---|
suite |
Top-level container for benchmarks |
setup <lang> |
Language-specific initialization (imports, helpers, init code) |
fixture |
Shared test data with portable hex encoding |
bench |
Individual benchmark definition |
globalSetup |
File-level initialization (e.g., spawning Anvil) |
use std::* |
Import standard library modules |
suite example {
description: "Suite description"
iterations: 5000 # Default iteration count
warmup: 100 # Warmup iterations before timing
baseline: "go" # Baseline language for comparisons
mode: "auto" # "auto" (calibrate to targetTime) or "fixed"
targetTime: 3000ms # Target time for auto mode
minIterations: 100 # Min iterations (auto mode)
maxIterations: 1000000 # Max iterations (auto mode)
count: 3 # Runs per benchmark for statistics
memory: true # Enable memory allocation profiling
concurrency: 4 # Parallel workers/goroutines
outlierDetection: true # IQR-based outlier removal
cvThreshold: 5.0 # Coefficient of variation target (%)
timeout: 30000 # Suite timeout (ms)
compare: true # Enable comparison tables
sink: true # Prevent dead code elimination
}
Setup blocks contain language-specific initialization with four sections:
setup go {
import "crypto/sha256"
import "encoding/hex"
declare {
var globalCounter int
type Config struct { Size int }
}
init {
globalCounter = 0
}
helpers {
func hash(data []byte) []byte {
h := sha256.Sum256(data)
return h[:]
}
}
}
Portable test data shared across languages:
# Hex-encoded data
fixture short_data {
hex: "deadbeef"
}
# Load from file
fixture long_data {
hex: @file("fixtures/1kb.hex")
}
# Language-specific implementations
fixture complex {
go: buildComplexStruct()
ts: buildComplexObject()
rust: build_complex_struct()
}
bench keccak256 {
description: "Keccak-256 hash"
iterations: 10000 # Override suite default
warmup: 500
timeout: 5000
tags: ["crypto", "hash"]
before {
go: prepareData()
ts: prepareData()
}
go: keccak256(data)
ts: keccak256(data_hex)
rust: keccak256(&data)
after {
charting.drawBarChart(title: "Keccak256 Performance")
}
validate {
go: len(result) == 32
ts: result.length === 66
}
}
before— Runs once before benchmark iterationsafter— Runs once after iterations (useful for charting)each— Runs before each iteration (outside timing)
use std::anvil # Ethereum node spawning
use std::charting # Chart generation
use std::constants # Mathematical constants
globalSetup {
anvil.spawnAnvil(fork: "https://eth-mainnet.g.alchemy.com/v2/...")
}
bench example {
go: doSomething(anvil.ANVIL_RPC_URL)
after {
charting.drawBarChart(title: "Results", horizontal: true)
charting.drawPieChart(title: "Distribution")
}
}
poly-bench includes a full-featured language server for editor integration.
| Feature | Description |
|---|---|
| Diagnostics | Parse errors, validation warnings, embedded language type checking |
| Formatting | Document formatting with proper indentation |
| Completions | Context-aware completions for keywords, stdlib, and user symbols |
| Hover | Documentation for DSL keywords and stdlib; delegates to gopls/tsserver/rust-analyzer for embedded code |
| Semantic Highlighting | Full syntax highlighting via semantic tokens |
| Embedded Language Support | Go, TypeScript, and Rust code blocks are checked by their respective language servers |
- VS Code / Cursor: Install from
extensions/vscodeor the marketplace - More editors coming soon
poly-bench lsp # Starts language server on stdiopoly-bench check <file> # Parse and validate
poly-bench run [<file>] # Execute benchmarks
poly-bench codegen <file> # Generate code without running
poly-bench fmt [<files>...] # Format .bench filespoly-bench init [<name>] # Initialize new project
poly-bench new <name> # Create benchmark template
poly-bench add --go <pkg> # Add Go dependency
poly-bench add --ts <pkg> # Add npm dependency
poly-bench add --rs <crate> # Add Rust crate
poly-bench install # Install dependencies
poly-bench build # Build runtime environmentpoly-bench run hash.bench \
--lang go \ # Run only Go
--iterations 100000 \ # Override iterations
--report markdown \ # Output format: console, markdown, json
--output results/ # Output directory═══════════════════════════════════════════════════════════════════
BENCHMARK RESULTS
═══════════════════════════════════════════════════════════════════
OVERALL SUMMARY
Go is 2.5x faster overall
Total Benchmarks: 10
Go Wins: 8 (80%)
TypeScript Wins: 2 (20%)
Generates benchmark-report.md with tables and statistics.
Structured output to benchmark-results.json for CI/automation.
Visual comparisons via charting.* directives in after blocks.
| Language | Runtime | Memory Profiling | Concurrency |
|---|---|---|---|
| Go | Subprocess + plugin | runtime.ReadMemStats |
Goroutines |
| TypeScript | Node.js | process.memoryUsage() |
— |
| Rust | Cargo subprocess | Supported | — |
- Go 1.21+ (for Go benchmarks)
- Node.js 18+ (for TypeScript benchmarks)
- Rust 1.70+ (for Rust benchmarks, or building from source)
One-liner:
curl -L https://raw.githubusercontent.com/evm-tooling/poly-bench/main/scripts/install.sh | bashFrom source:
cargo install --path .Upgrade:
poly-bench upgradeReleases are created via the Makefile:
make release VERSION=v0.1.0This bumps versions in Cargo.toml and extensions/vscode/package.json, creates a git tag, and triggers the GitHub release workflow.
MIT License — see LICENSE for details.