How do I profile an EK9 program?

← Profiling · Ref: Q627

EK9 has built-in profiling. Append 'p' to any test flag to enable performance profiling.

BASIC PROFILING

  ek9 -tp myproject.ek9

This runs all tests with profiling instrumentation and produces a human-readable summary showing call counts and timing for each function and method.

PROFILING OUTPUT FORMATS

The 'p' suffix works with every test format:

  ek9 -tp  myproject.ek9   Human-readable profiling summary.
  ek9 -t0p myproject.ek9   Terse profiling (CI pass/fail).
  ek9 -t2p myproject.ek9   JSON profiling data (for AI and tools).
  ek9 -t3p myproject.ek9   JUnit XML with profiling annotations.
  ek9 -t6p myproject.ek9   HTML dashboard with flame graph.

WHAT GETS MEASURED

For each function and method the profiler records:
- Call count: how many times it was invoked.
- Total time: wall-clock time including calls to other functions.
- Self time: time in this function only, excluding callees.
- Average, minimum, and maximum call times.
- Percentile distribution: p50, p95, p99.

NO OPTIMISATION

Profiling automatically forces -O0 (no optimisation). This ensures that probe-to-source mapping is accurate. Every function call is measured as written, with no inlining or dead code elimination.

See Q628 for profiling tests specifically. See Q629 for reading flame graphs. See Q630 for identifying hot methods. See Q631 for benchmarking two approaches. See Q322 for profiling overview. See Q207 for test output formats. See Q752 for fuzz HTML dashboard (similar report pattern).

Example

defines module qa.profilingdeep.program

  defines function

    <?-
      Recursive function that shows up as a hot method in profiling.
      The profiler records call count and self-time for each invocation.
    -?>
    factorial() as pure
      -> number as Integer
      <- result as Integer: 1

      recursionBase <- 1
      if number > recursionBase
        subResult <- factorial(number - 1)
        result: number * subResult

    sumUpTo() as pure
      -> limit as Integer
      <- total as Integer: 0

      for i in 1 ... limit
        total: total + i

  defines program

    ProfileProgramDemo()
      stdout <- Stdout()

      tenFactorial <- 10
      result <- factorial(tenFactorial)
      stdout.println(`10! = ${result}`)

      hundredSum <- 100
      sum <- sumUpTo(hundredSum)
      stdout.println(`Sum 1..100 = ${sum}`)

Common mistakes

E50001 — Removing the variable declaration means later references to the variable become unresolved, triggering E50001. See ek9 -h E50001 for details.

Incorrect:

factorial(tenFactorial)

Correct:

result <- factorial(tenFactorial)
Other ways to ask this
  • How do I find performance bottlenecks in EK9?
  • What is the ek9 -tp flag for?
  • How do I measure function call times in EK9?

Coming from another language?

Java: async-profiler, JFR, or VisualVM (all external tools, require JVM flags). Python: cProfile (stdlib), py-spy (external, sampling). Rust: perf, flamegraph crate (external). Go: go tool pprof with -cpuprofile flag (built-in but requires code changes). JavaScript: Chrome DevTools profiler (browser only). EK9: append 'p' to any test flag for built-in profiling, zero code changes, multiple output formats including JSON and HTML flame graph.

Keywords: call, program, bottleneck, total, performance, time, profile, count, tp, self, profiling, flame-graph, O0