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