benchee-profiling
Skill4 starsChanged 4 months agoMITView on GitHub
MANDATORY when profiling and benchmarking Elixir code, or before optimizing performance-critical code. Sets up Benchee benchmarks, measures execution time, compares function implementations, generates profiling reports with :fprof and :eprof, and integrates benchmark regression checks into CI pipelines. Trigger words: Benchee, benchmark, profiling, performance, optimization, speed, comparison,
In this repository34 other files it gives its agents.
- AGENTS.mdAGENTS.md
- apply-phoenix-controller-conventionsSkill
- ash-frameworkSkill
- broadway-data-pipelinesSkill
- cachex-cachingSkill
- code-qualitySkill
- code-reviewSkill
- deployment-gotchasSkill
- ecto-changeset-patternsSkill
- ecto-essentialsSkill
- ecto-migrationSkill
- ecto-nested-associationsSkill
- elixir-essentialsSkill
- gettext-i18nSkill
- liveview-streamsSkill
- mix-tasks-generatorsSkill
- oban-essentialsSkill
- otp-essentialsSkill
- phoenix-authenticationSkill
- phoenix-authorization-patternsSkill
- phoenix-channels-essentialsSkill
- phoenix-json-apiSkill
- phoenix-liveview-essentialsSkill
- phoenix-pubsub-patternsSkill
- phoenix-uploadsSkill
- property-based-testingSkill
- refactor-codeSkill
- req-http-clientSkill
- respond-to-reviewSkill
- security-essentialsSkill
- swoosh-emailsSkill
- telemetry-essentialsSkill
- testing-essentialsSkill
- typespec-dialyzerSkill
The file
We split this skill into the rules it gives an agent
2 rules. About 0 are common, 2 are distinctive, and 0 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈2
Distinctive · estimate
This author's own choices. Worth a look.
0
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 49
Use this pattern as the starting point for any new benchmark — it covers timing, memory, multiple inputs, and formatted output in one call.
- Distinctive: line 82
Use this pattern when you have multiple concrete implementations of the same operation and need to confirm which is fastest across …
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Benchee Profiling
RULES — Follow these with no exceptions
1. Profile first — run :fprof or :eprof and verify the output explicitly names the expected slow call site; re-run with a larger workload if ambiguous
2. Write a comparative benchmark — implement at least 2 alternative approaches using Benchee, ensuring implementations do the same thing; benchmark in MIX_ENV=prod for realistic results
3. Use multiple inputs — test with small, medium, and large realistic data sizes to catch size-dependent behavior
4. Warm up before measuring — use warmup: 2 and time: 10; repeat 3–5 times to rule out variance; if results are within noise (< 5% difference), run 3 additional times
5. Validate improvement — confirm the faster approach wins across all input sizes
6. Save baseline and check regressions — write results to bench/baseline.json; raise an error if performance degrades more than 10% (compare against the previous 3 baselines before raising to rule out noise)
7. Separate I/O benchmarks — never benchmark network or disk I/O in the same run as compute benchmarks
Setup
# mix.exs
defp deps do
[
{:benchee, "~> 1.3", only: :dev}
]
endBasic Benchmark with Full Configuration
Use this pattern as the starting point for any new benchmark — it covers timing, memory, multiple inputs, and formatted output in one call.
# bench/list_benchmark.exs
Benchee.run(
%{
"Enum.sort" => fn list -> Enum.sort(list) end,
"Enum.sort_by" => fn list -> Enum.sort_by(list, & &1) end
},
time: 10, # Run each scenario for 10 seconds
warmup: 2, # Warm up for 2 seconds
memory_time: 2, # Measure memory usage
reduction_time: 2, # Measure reductions
inputs: %{
"small list" => Enum.to_list(1..100),
"medium list" => Enum.to_list(1..10_000),
"large list" => Enum.to_list(1..100_000)
},
formatters: [
{Benchee.Formatters.Console, comparison: true},
{Benchee.Formatters.HTML, file: "output/benchmark.html"}
]
)# Run benchmark
MIX_ENV=prod mix run bench/list_benchmark.exsComparing Real-World Implementations
Use this pattern when you have multiple concrete implementations of the same operation and need to confirm which is fastest across realistic data. Unlike the basic example above, this section demonstrates benchmarking non-trivial logic defined in a module.
defmodule StringOperations do
def concat_loop(strings) do
Enum.reduce(strings, "", fn s, acc -> acc <> s end)
end
def concat_join(strings) do
Enum.join(strings)
end
def concat_comprehension(strings) do
for s <- strings, into: "", do: s
end
end
strings = for i <- 1..1000, do: "string_#{i}"
Benchee.run(%{
"reduce" => fn -> StringOperations.concat_loop(strings) end,
"join" => fn -> StringOperations.concat_join(strings) end,
"comprehension" => fn -> StringOperations.concat_comprehension(strings) end
})Profiling with :fprof
:fprof.trace(:start, file: 'trace.trace')
MyApp.SlowFunction.run()
:fprof.trace(:stop)
:fprof.profile(file: 'trace.trace')
:fprof.analyse(dest: 'analysis.txt')Profiling with :eprof
:eprof.start()
:eprof.start_profiling([self()])
MyApp.SlowFunction.run()
:eprof.stop_profiling()
:eprof.analyze()
:eprof.stop()Benchmark Suite Organization
bench/
├── string_benchmark.exs # String operations
├── list_benchmark.exs # List operations
├── json_benchmark.exs # JSON encoding/decoding
├── suite.exs # Run all benchmarks
└── baseline.json # Baseline for regression detection# bench/suite.exs
Code.require_file("bench/string_benchmark.exs")
Code.require_file("bench/list_benchmark.exs")
Code.require_file("bench/json_benchmark.exs")Advanced Topics
CI Integration
# .github/workflows/benchmark.yml
name: Benchmark
on:
push:
branches: [main]
jobs:
benchmark:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Run benchmarks
run: |
mix deps.get
# Write JSON via Benchee.Formatters.JSON in suite.exs (not a mix run flag)
MIX_ENV=prod mix run bench/suite.exs
- name: Store results
uses: actions/upload-artifact@v3
with:
name: benchmark-results
path: bench/results.jsonRegression Comparison Script
# bench/compare_with_baseline.exs
baseline_file = "bench/baseline.json"
results =
Benchee.run(
%{
"current" => fn -> MyApp.FastFunction.run() end
},
time: 5,
formatters: [{Benchee.Formatters.Console, comparison: true}]
)
scenario_ips = fn suite ->
suite.scenarios
|> hd()
|> then(fn scenario -> scenario.run_time_data.statistics.ips end)
end
current_ips = scenario_ips.(results)
if File.exists?(baseline_file) do
baseline = File.read!(baseline_file) |> Jason.decode!()
baseline_ips = baseline["ips"]
regression = (baseline_ips - current_ips) / baseline_ips * 100
if regression > 10 do
Mix.raise("Performance regression detected: #{regression}% slower")
end
end
File.write!(baseline_file, Jason.encode!(%{ips: current_ips}))Memory Profiling
Benchee.run(
%{
"String manipulation" => fn ->
list = for i <- 1..1000, do: "item_#{i}"
Enum.join(list, ",")
end,
"Binary manipulation" => fn ->
list = for i <- 1..1000, do: "item_#{i}"
IO.iodata_to_binary(Enum.intersperse(list, ","))
end
},
memory_time: 5,
reduction_time: 5
)Common Pitfalls
| ❌ Don't | ✅ Do |
Benchmark in MIX_ENV=dev | Run with MIX_ENV=prod mix run bench/... for realistic numbers |
| Measure with no warmup | Set warmup: 2 and time: 10 before measuring |
| Compare approaches that return different results | Verify every implementation produces identical output first |
| Optimize before profiling | Run :fprof/:eprof to confirm the real hotspot |
| Benchmark a single input size | Use small/medium/large inputs to catch scaling behavior |
| Mix I/O and compute in one run | Separate network/disk benchmarks from CPU benchmarks |
| Fail CI on one noisy run | Compare against the last 3 baselines before raising a regression |
Integration
| Predecessor | This Skill | Successor |
| telemetry-essentials | benchee-profiling | deployment-gotchas |
| code-quality | benchee-profiling | None (standalone) |
Companion skills: telemetry-essentials, deployment-gotchas, code-quality.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

