How do I profile my EK9 tests?
← Profiling · Ref: Q628
Every test format supports profiling by appending 'p'. The profiler instruments all code paths exercised by your tests.
PROFILE ALL TESTS
ek9 -tp myproject.ek9
Runs all @Test functions with profiling. The human-readable output lists each function with call count and timing, sorted by self-time.
JSON OUTPUT FOR ANALYSIS
ek9 -t2p myproject.ek9
Produces structured JSON that includes profiling data per function. This is ideal for:
- AI assistants analysing performance.
- CI pipelines checking latency thresholds.
- Trend analysis tools tracking performance over builds.
HTML DASHBOARD
ek9 -t6p myproject.ek9
Generates an interactive HTML page with:
- Flame graph visualisation.
- Hot function table sorted by self-time.
- Call tree with timing annotations.
- Source views with timing badges per function.
FINDING SLOW TESTS
The profiler measures all code called during test execution. If a test is slow, the profiler shows which function under that test consumed the most time. This distinguishes between:
- Slow test setup (the test infrastructure).
- Slow application code (the code under test).
- Excessive assertions (too many checks per test).
FILTERING
Profile specific test groups:
ek9 -tp -tg performance myproject.ek9
This profiles only tests in the 'performance' group.
See Q627 for profiling programs. See Q629 for reading flame graphs. See Q630 for identifying hot methods. See Q155 for writing unit tests. See Q157 for running tests. See Q207 for test output formats.
Example
defines module qa.profilingdeep.tests defines function <?- Functions that tests would exercise. Profiling reveals which functions consume the most time. -?> isPrime() -> candidate as Integer <- prime as Boolean: false minimumPrime <- 2 if candidate >= minimumPrime prime: true divisor <- 2 squaredDivisor <- divisor * divisor while squaredDivisor <= candidate and prime if candidate mod divisor == 0 prime: false divisor: divisor + 1 squaredDivisor: divisor * divisor countPrimesBelow() -> limit as Integer <- count as Integer: 0 for candidate in 2 ... limit if isPrime(candidate) count: count + 1 defines program ProfileTestsDemo() stdout <- Stdout() hundredLimit <- 100 primeCount <- countPrimesBelow(hundredLimit) stdout.println(`Primes below 100: ${primeCount}`)
Common mistakes
E50060 — Integer has no toString() method in EK9; `primeCount.toString()` triggers E50060 - use the $ prefix operator or `${primeCount}` interpolation. See ek9 -h E50060 for details.
Incorrect:
stdout.println(primeCount.toString())
Correct:
stdout.println(`Primes below 100: ${primeCount}`)
E50060 — Integer has no intValue() method in EK9. Integer values are used directly. See ek9 -h E50060 for details.
Incorrect:
primeCount <- countPrimesBelow(hundredLimit).intValue()
Correct:
primeCount <- countPrimesBelow(hundredLimit)
Other ways to ask this
- How do I find slow tests in EK9?
- What does ek9 -t2p output look like?
- How do I get JSON profiling data from EK9 tests?
Coming from another language?
Java: JUnit + JMH for microbenchmarks (separate framework), async-profiler for test profiling (external tool). Python: pytest-benchmark (plugin), cProfile with test runner (manual setup). Rust: criterion for benchmarks (external crate), no built-in test profiling. Go: go test -bench for benchmarks, -cpuprofile for profiling (separate flags). EK9: append 'p' to any test flag, profiling is built into the test runner, no separate framework needed.
Keywords: t2p, test, flame, t6p, flame-graph, group, JSON, dashboard, slow, HTML, benchmark, profile, performance