Skip to content

Latency — v0.9.0

The latency-characterization tier stands beside correctness. Fresh cold timing sidecars and prerequisite warm and invocation-overhead controls are retained under their versioned recorder paths. Missing timing is not invented.

The full retained ranges are published. Cache state remained uncontrolled, and these desktop observations establish no causal cross-release speedup.

What these numbers are, and what they are not

Section titled “What these numbers are, and what they are not”

This is a descriptive characterization of per-case analyzer wall-clock, published under the contract preregistered in docs/latency-tier.md, which was merged before a single timestamp was captured. It is not a score, not a ranking, and not a benchmark result in the sense the rest of this site uses that phrase.

  • Latency is never pooled with correctness. There is no combined number anywhere on this site, no efficiency-adjusted rate, and no leaderboard that blends the two. "Correct but slow" and "fast but wrong" stay independently visible. No correctness outcome in this freeze was derived from, conditioned on, or tie-broken by a timing value.
  • A latency number is a property of a run, not of a case. No case carries a timing assertion, threshold, or budget.
  • Characterization, not micro-benchmark. There are no repeated trials, no warm-up iterations, and no steady-state harness in the cold figures below. The case population is the sample and its spread is the statistics. The distribution columns below are that spread — not error bars on a measurement that was repeated, because it was not. (The warm-marginal section is the one place a series is measured more than once. It says so, retains every repeat, and publishes the range they span rather than averaging them into a statistic.)
  • These are the conditions the numbers were produced under: a single developer machine, under light concurrent load, running the benchmark's standing sequential-run discipline — one analyzer at a time, never two at once. That discipline is what makes the numbers usable at all; the light concurrent load is what stops them being a controlled measurement. Read them as characterization of what this benchmark actually costs to run, at the order-of-magnitude and shape level, and not as a precise figure for any engine.

Recorded once per run beside the tool identity the run witnessed, and shown here verbatim. Latency numbers are comparable within one environment and are not comparable across machines. Every published run to date executes on one maintainer machine, and that caveat travels with every number on this page.

Hardware modelOSOS releaseLogical CPUsCPU architectureTimed runs stamped
Mac16,1macos27.0.010aarch6471

One row: every timed invocation in this bound latency corpus ran in the same stamped environment, so no number on this page is a cross-environment comparison. Were there two rows, this page would say so in the table rather than averaging across them. The count is of run directories that contain at least one timed invocation — a run that invoked no analyzer at all still stamps its environment and witnesses its tool identity, but contributes no latency number and is not counted here.

The granularity is unequal, and that is the point

Section titled “The granularity is unequal, and that is the point”

A phase is timed if and only if the adapter already invokes it as a separate subprocess. The benchmark never instruments analyzer internals, never patches an upstream tool, and never infers a boundary a subprocess boundary does not expose. The adapters do not expose the same boundaries, so the rows below are not equally decomposed — and the two ways of papering over that would both misdescribe the evidence: summing a decomposed adapter's phases and presenting every row as one number of the same kind, or guessing phase splits for the adapters that expose only one.

So the table states each adapter's declared granularity as data. Phases compare within an adapter; analyzer-invocation totals compare across adapters. A CodeQL database-create number and a Joern total are not the same kind of quantity, and nothing on this page sets them side by side as though they were.

AdapterVersionObserved subprocesses per casePhases timedTimed invocations
Bifrost0.12.00 or 1total 1031
FlowDroid2.15.10 or 1 or 3total compile dex analyze 154
Semgrep CE1.177.00 or 1total 196
Inferv1.3.00 or 2capture analyze 204
Pysa0.10.01total 102
OpenTaintv0.4.60 or 1total 152
Joern4.0.6280 or 1total 460
CodeQL2.27.10 or 2database-create database-analyze 858

3 of 8 adapters expose more than one subprocess and are decomposed here (FlowDroid and Infer and CodeQL); the other 5 expose a single invocation and take one honestly labelled number, with interpreter or JVM start-up inside it. That is a fact about invocation shapes, not about engine architecture: an adapter with one number is not an adapter that does one thing.

The phase names above are the ones the retained evidence carries, so any name here can be found in the artifact it came from. For CodeQL those are database-create and database-analyze, after the subcommands that produce them, where the preregistration's table spells the same two boundaries extract and analyze. Two spellings, one pair of subprocess boundaries: nothing moved and nothing is attributed differently. The contract is immutable, so the difference is recorded in its Amendment A12 rather than edited out of its table.

Analyzer-invocation wall-clock, per adapter

Section titled “Analyzer-invocation wall-clock, per adapter”

The only cross-adapter reading this tier supports: each adapter's cold per-case analyzer-invocation wall-clock, over every case in this snapshot's explicitly bound latency population that it actually invoked. Adapter-observable setup phases remain visible below, but only phases the contract classifies as analyzer work enter this total. Per-case timing at this granularity includes per-invocation fixed costs — JVM start-up, extractor initialization, interpreter start — that a long-lived deployment of the same engine would amortize. This tier does not correct for that; it characterizes what the benchmark actually runs.

Fastest to slowest, and the same ranking per kernel

Ordered by median, on a logarithmic axis, with the spread drawn beside every median rather than left to the table. The toggle scopes the same ranking to one language kernel at a time: a whole-corpus median mixes the languages an adapter runs on, and holding the language fixed is the difference between "this engine is slow" and "this front end is expensive". Only the analyzers that actually invoked on a kernel appear in its view. The chart draws the median, the quartiles and the p10–p90 spread; the disclosure beneath it opens the same distributions as a table, with the minima and maxima the whiskers deliberately leave out.

Cold per-case analyzer-invocation wall-clock over the bound latency population One row per analyzer, ordered fastest median first, on a logarithmic time axis. Each row draws the tenth to ninetieth percentile as a thin line, the interquartile range as a bar, and the median as a thick tick, with the median also printed as a number. Indented rows are the declared phases of adapters whose invocation exposes more than one subprocess, and belong to the adapter above them only. Materialization phases are explicitly labelled and excluded from the analyzer total. The latency page carries every value, including the minima and maxima this chart does not draw, in the data tables behind its "Show the data table" disclosures. A solid caret below a row is that adapter's separately measured warm marginal per case; a dashed span above a row is its estimated per-invocation overhead, an upper bound measured on a trivial no-flow fixture, drawn across the range its repeats spanned rather than at a point. Neither is a cold median, neither affects the ordering, and neither is subtracted from anything. 30 ms 100 ms 300 ms 1 s 3 s 10 s 30 s 100 s Wall-clock per invocation, logarithmic scale Bifrost — median 126 ms, IQR 114 ms to 227 ms, p10–p90 109 ms to 9.34 s, over 1031 timed analyzer invocations. Estimated per-invocation overhead, an upper bound from a trivial no-flow python fixture: 851 ms to 1.20 s across repeats Bifrost 1031 timed 126 ms FlowDroid — median 673 ms, IQR 632 ms to 844 ms, p10–p90 595 ms to 873 ms, over 154 timed analyzer invocations. Estimated per-invocation overhead, an upper bound from a trivial no-flow java fixture: 608 ms to 637 ms across repeats FlowDroid 154 timed 673 ms FlowDroid phase total — median 740 ms, IQR 641 ms to 846 ms, p10–p90 625 ms to 873 ms. Included in this adapter's analyzer total; phase detail compares only within the adapter. total 740 ms FlowDroid phase compile — median 448 ms, IQR 444 ms to 465 ms, p10–p90 435 ms to 470 ms. Materialization phase excluded from the analyzer total by contract. compile · excluded 448 ms FlowDroid phase dex — median 300 ms, IQR 298 ms to 312 ms, p10–p90 296 ms to 317 ms. Materialization phase excluded from the analyzer total by contract. dex · excluded 300 ms FlowDroid phase analyze — median 515 ms, IQR 506 ms to 523 ms, p10–p90 486 ms to 528 ms. Included in this adapter's analyzer total; phase detail compares only within the adapter. analyze 515 ms Semgrep CE — median 813 ms, IQR 809 ms to 819 ms, p10–p90 807 ms to 837 ms, over 196 timed analyzer invocations. Estimated per-invocation overhead, an upper bound from a trivial no-flow kotlin fixture: 1.34 s to 1.42 s across repeats Semgrep CE 196 timed 813 ms Infer — median 1.11 s, IQR 268 ms to 3.23 s, p10–p90 263 ms to 3.27 s, over 204 timed analyzer invocations. Estimated per-invocation overhead, an upper bound from a trivial no-flow c fixture: 275 ms to 336 ms across repeats Infer 204 timed 1.11 s Infer phase capture — median 918 ms, IQR 76 ms to 3.03 s, p10–p90 75 ms to 3.07 s. Included in this adapter's analyzer total; phase detail compares only within the adapter. capture 918 ms Infer phase analyze — median 193 ms, IQR 190 ms to 196 ms, p10–p90 188 ms to 201 ms. Included in this adapter's analyzer total; phase detail compares only within the adapter. analyze 193 ms Pysa — median 2.49 s, IQR 2.47 s to 2.53 s, p10–p90 2.45 s to 2.57 s, over 102 timed analyzer invocations. Estimated per-invocation overhead, an upper bound from a trivial no-flow python fixture: 2.48 s to 2.56 s across repeats Pysa 102 timed 2.49 s OpenTaint — median 3.80 s, IQR 3.71 s to 3.93 s, p10–p90 3.63 s to 4.04 s, over 152 timed analyzer invocations. Estimated per-invocation overhead, an upper bound from a trivial no-flow kotlin fixture: 3.76 s to 4.56 s across repeats OpenTaint 152 timed 3.80 s Joern — median 4.03 s, IQR 3.75 s to 5.29 s, p10–p90 3.63 s to 7.98 s, over 460 timed analyzer invocations. Estimated per-invocation overhead, an upper bound from a trivial no-flow php fixture: 3.80 s to 4.20 s across repeats Joern 460 timed 4.03 s CodeQL — median 7.32 s, IQR 5.19 s to 14.4 s, p10–p90 4.98 s to 19.2 s, over 858 timed analyzer invocations. Estimated per-invocation overhead, an upper bound from a trivial no-flow ruby fixture: 3.78 s to 4.28 s across repeats CodeQL 858 timed 7.32 s CodeQL phase database-create — median 3.06 s, IQR 1.94 s to 9.67 s, p10–p90 1.21 s to 10.8 s. Included in this adapter's analyzer total; phase detail compares only within the adapter. database-create 3.06 s CodeQL phase database-analyze — median 4.28 s, IQR 3.30 s to 4.80 s, p10–p90 2.87 s to 8.56 s. Included in this adapter's analyzer total; phase detail compares only within the adapter. database-analyze 4.28 s

Every timed analyzer invocation the freeze binds — 3157 of them, across every score tier and both model profiles. This is the widest denominator on the site and the only one here that is not a single population: an adapter's median mixes the languages it runs on, whose fixtures differ in size and whose front ends differ in cost. The per-kernel views hold the language fixed.

Cold per-case analyzer-invocation wall-clock on the c kernel One row per analyzer, ordered fastest median first, on a logarithmic time axis. Each row draws the tenth to ninetieth percentile as a thin line, the interquartile range as a bar, and the median as a thick tick, with the median also printed as a number. Indented rows are the declared phases of adapters whose invocation exposes more than one subprocess, and belong to the adapter above them only. Materialization phases are explicitly labelled and excluded from the analyzer total. The latency page carries every value, including the minima and maxima this chart does not draw, in the data tables behind its "Show the data table" disclosures. A solid caret below a row is that adapter's separately measured warm marginal per case; a dashed span above a row is its estimated per-invocation overhead, an upper bound measured on a trivial no-flow fixture, drawn across the range its repeats spanned rather than at a point. Neither is a cold median, neither affects the ordering, and neither is subtracted from anything. 30 ms 100 ms 300 ms 1 s 3 s 10 s 30 s 100 s Wall-clock per invocation, logarithmic scale Bifrost — median 131 ms, IQR 124 ms to 140 ms, p10–p90 118 ms to 155 ms, over 56 timed analyzer invocations Bifrost 56 of 56 timed 131 ms Infer — median 266 ms, IQR 263 ms to 276 ms, p10–p90 261 ms to 291 ms, over 56 timed analyzer invocations. Estimated per-invocation overhead, an upper bound from a trivial no-flow c fixture: 275 ms to 336 ms across repeats Infer 56 of 56 timed 266 ms Infer phase capture — median 75 ms, IQR 75 ms to 77 ms, p10–p90 74 ms to 91 ms. Included in this adapter's analyzer total; phase detail compares only within the adapter. capture 75 ms Infer phase analyze — median 189 ms, IQR 187 ms to 192 ms, p10–p90 187 ms to 198 ms. Included in this adapter's analyzer total; phase detail compares only within the adapter. analyze 189 ms Semgrep CE — median 816 ms, IQR 814 ms to 829 ms, p10–p90 813 ms to 865 ms, over 14 timed analyzer invocations Semgrep CE 14 of 56 timed 816 ms CodeQL — median 5.06 s, IQR 5.01 s to 5.12 s, p10–p90 5.00 s to 5.32 s, over 56 timed analyzer invocations CodeQL 56 of 56 timed 5.06 s CodeQL phase database-create — median 1.21 s, IQR 1.21 s to 1.23 s, p10–p90 1.20 s to 1.40 s. Included in this adapter's analyzer total; phase detail compares only within the adapter. database-create 1.21 s CodeQL phase database-analyze — median 3.84 s, IQR 3.80 s to 3.91 s, p10–p90 3.78 s to 3.99 s. Included in this adapter's analyzer total; phase detail compares only within the adapter. database-analyze 3.84 s

The c kernel's 56 benchmark-controlled core assertions, the same no-pooling population the correctness sections read. 4 of the 8 analyzers invoked here at all; the rest do not cover this kernel and are absent from this view rather than drawn at zero. Where a row's count is below 56, the remaining assertions were declined before any subprocess was spawned and so have nothing to time.

Cold per-case analyzer-invocation wall-clock on the cpp kernel One row per analyzer, ordered fastest median first, on a logarithmic time axis. Each row draws the tenth to ninetieth percentile as a thin line, the interquartile range as a bar, and the median as a thick tick, with the median also printed as a number. Indented rows are the declared phases of adapters whose invocation exposes more than one subprocess, and belong to the adapter above them only. Materialization phases are explicitly labelled and excluded from the analyzer total. The latency page carries every value, including the minima and maxima this chart does not draw, in the data tables behind its "Show the data table" disclosures. A solid caret below a row is that adapter's separately measured warm marginal per case; a dashed span above a row is its estimated per-invocation overhead, an upper bound measured on a trivial no-flow fixture, drawn across the range its repeats spanned rather than at a point. Neither is a cold median, neither affects the ordering, and neither is subtracted from anything. 30 ms 100 ms 300 ms 1 s 3 s 10 s 30 s 100 s Wall-clock per invocation, logarithmic scale Bifrost — median 131 ms, IQR 123 ms to 142 ms, p10–p90 119 ms to 167 ms, over 68 timed analyzer invocations Bifrost 68 of 68 timed 131 ms Infer — median 274 ms, IQR 269 ms to 1.15 s, p10–p90 267 ms to 1.47 s, over 68 timed analyzer invocations Infer 68 of 68 timed 274 ms Infer phase capture — median 77 ms, IQR 76 ms to 945 ms, p10–p90 75 ms to 1.28 s. Included in this adapter's analyzer total; phase detail compares only within the adapter. capture 77 ms Infer phase analyze — median 195 ms, IQR 193 ms to 198 ms, p10–p90 191 ms to 201 ms. Included in this adapter's analyzer total; phase detail compares only within the adapter. analyze 195 ms Semgrep CE — median 812 ms, IQR 810 ms to 815 ms, p10–p90 809 ms to 823 ms, over 14 timed analyzer invocations Semgrep CE 14 of 68 timed 812 ms CodeQL — median 5.11 s, IQR 5.05 s to 13.9 s, p10–p90 5.01 s to 17.1 s, over 68 timed analyzer invocations CodeQL 68 of 68 timed 5.11 s CodeQL phase database-create — median 1.23 s, IQR 1.21 s to 7.64 s, p10–p90 1.21 s to 9.88 s. Included in this adapter's analyzer total; phase detail compares only within the adapter. database-create 1.23 s CodeQL phase database-analyze — median 3.89 s, IQR 3.83 s to 6.37 s, p10–p90 3.79 s to 7.30 s. Included in this adapter's analyzer total; phase detail compares only within the adapter. database-analyze 3.89 s

The cpp kernel's 68 benchmark-controlled core assertions, the same no-pooling population the correctness sections read. 4 of the 8 analyzers invoked here at all; the rest do not cover this kernel and are absent from this view rather than drawn at zero. Where a row's count is below 68, the remaining assertions were declined before any subprocess was spawned and so have nothing to time.

Cold per-case analyzer-invocation wall-clock on the csharp kernel One row per analyzer, ordered fastest median first, on a logarithmic time axis. Each row draws the tenth to ninetieth percentile as a thin line, the interquartile range as a bar, and the median as a thick tick, with the median also printed as a number. Indented rows are the declared phases of adapters whose invocation exposes more than one subprocess, and belong to the adapter above them only. Materialization phases are explicitly labelled and excluded from the analyzer total. The latency page carries every value, including the minima and maxima this chart does not draw, in the data tables behind its "Show the data table" disclosures. A solid caret below a row is that adapter's separately measured warm marginal per case; a dashed span above a row is its estimated per-invocation overhead, an upper bound measured on a trivial no-flow fixture, drawn across the range its repeats spanned rather than at a point. Neither is a cold median, neither affects the ordering, and neither is subtracted from anything. 30 ms 100 ms 300 ms 1 s 3 s 10 s 30 s 100 s Wall-clock per invocation, logarithmic scale Bifrost — median 127 ms, IQR 121 ms to 137 ms, p10–p90 118 ms to 144 ms, over 70 timed analyzer invocations Bifrost 70 of 70 timed 127 ms CodeQL — median 18.8 s, IQR 18.5 s to 19.2 s, p10–p90 18.3 s to 19.6 s, over 70 timed analyzer invocations CodeQL 70 of 70 timed 18.8 s CodeQL phase database-create — median 10.5 s, IQR 10.3 s to 10.8 s, p10–p90 10.1 s to 11.1 s. Included in this adapter's analyzer total; phase detail compares only within the adapter. database-create 10.5 s CodeQL phase database-analyze — median 8.26 s, IQR 8.11 s to 8.48 s, p10–p90 8.02 s to 8.78 s. Included in this adapter's analyzer total; phase detail compares only within the adapter. database-analyze 8.26 s

The csharp kernel's 70 benchmark-controlled core assertions, the same no-pooling population the correctness sections read. 2 of the 8 analyzers invoked here at all; the rest do not cover this kernel and are absent from this view rather than drawn at zero. Where a row's count is below 70, the remaining assertions were declined before any subprocess was spawned and so have nothing to time.

Cold per-case analyzer-invocation wall-clock on the go kernel One row per analyzer, ordered fastest median first, on a logarithmic time axis. Each row draws the tenth to ninetieth percentile as a thin line, the interquartile range as a bar, and the median as a thick tick, with the median also printed as a number. Indented rows are the declared phases of adapters whose invocation exposes more than one subprocess, and belong to the adapter above them only. Materialization phases are explicitly labelled and excluded from the analyzer total. The latency page carries every value, including the minima and maxima this chart does not draw, in the data tables behind its "Show the data table" disclosures. A solid caret below a row is that adapter's separately measured warm marginal per case; a dashed span above a row is its estimated per-invocation overhead, an upper bound measured on a trivial no-flow fixture, drawn across the range its repeats spanned rather than at a point. Neither is a cold median, neither affects the ordering, and neither is subtracted from anything. 30 ms 100 ms 300 ms 1 s 3 s 10 s 30 s 100 s Wall-clock per invocation, logarithmic scale Bifrost — median 159 ms, IQR 151 ms to 170 ms, p10–p90 147 ms to 176 ms, over 70 timed analyzer invocations Bifrost 70 of 70 timed 159 ms Semgrep CE — median 813 ms, IQR 811 ms to 815 ms, p10–p90 809 ms to 818 ms, over 14 timed analyzer invocations Semgrep CE 14 of 70 timed 813 ms CodeQL — median 5.73 s, IQR 5.66 s to 5.82 s, p10–p90 5.62 s to 5.98 s, over 70 timed analyzer invocations CodeQL 70 of 70 timed 5.73 s CodeQL phase database-create — median 3.06 s, IQR 3.01 s to 3.14 s, p10–p90 2.98 s to 3.23 s. Included in this adapter's analyzer total; phase detail compares only within the adapter. database-create 3.06 s CodeQL phase database-analyze — median 2.67 s, IQR 2.61 s to 2.70 s, p10–p90 2.59 s to 2.81 s. Included in this adapter's analyzer total; phase detail compares only within the adapter. database-analyze 2.67 s

The go kernel's 70 benchmark-controlled core assertions, the same no-pooling population the correctness sections read. 3 of the 8 analyzers invoked here at all; the rest do not cover this kernel and are absent from this view rather than drawn at zero. Where a row's count is below 70, the remaining assertions were declined before any subprocess was spawned and so have nothing to time.

Cold per-case analyzer-invocation wall-clock on the java kernel One row per analyzer, ordered fastest median first, on a logarithmic time axis. Each row draws the tenth to ninetieth percentile as a thin line, the interquartile range as a bar, and the median as a thick tick, with the median also printed as a number. Indented rows are the declared phases of adapters whose invocation exposes more than one subprocess, and belong to the adapter above them only. Materialization phases are explicitly labelled and excluded from the analyzer total. The latency page carries every value, including the minima and maxima this chart does not draw, in the data tables behind its "Show the data table" disclosures. A solid caret below a row is that adapter's separately measured warm marginal per case; a dashed span above a row is its estimated per-invocation overhead, an upper bound measured on a trivial no-flow fixture, drawn across the range its repeats spanned rather than at a point. Neither is a cold median, neither affects the ordering, and neither is subtracted from anything. 30 ms 100 ms 300 ms 1 s 3 s 10 s 30 s 100 s Wall-clock per invocation, logarithmic scale FlowDroid — median 641 ms, IQR 627 ms to 667 ms, p10–p90 616 ms to 672 ms, over 70 timed analyzer invocations. Estimated per-invocation overhead, an upper bound from a trivial no-flow java fixture: 608 ms to 637 ms across repeats FlowDroid 70 of 70 timed 641 ms Semgrep CE — median 812 ms, IQR 809 ms to 813 ms, p10–p90 808 ms to 831 ms, over 14 timed analyzer invocations. Warm marginal, measured separately: 710 ms to 732 ms per case in one process, over 2 retained repeats Semgrep CE 14 of 70 timed 812 ms Infer — median 3.24 s, IQR 3.22 s to 3.27 s, p10–p90 3.21 s to 3.29 s, over 70 timed analyzer invocations Infer 70 of 70 timed 3.24 s Infer phase capture — median 3.04 s, IQR 3.03 s to 3.07 s, p10–p90 3.01 s to 3.10 s. Included in this adapter's analyzer total; phase detail compares only within the adapter. capture 3.04 s Infer phase analyze — median 194 ms, IQR 191 ms to 196 ms, p10–p90 190 ms to 200 ms. Included in this adapter's analyzer total; phase detail compares only within the adapter. analyze 194 ms OpenTaint — median 3.73 s, IQR 3.66 s to 3.79 s, p10–p90 3.61 s to 3.95 s, over 70 timed analyzer invocations OpenTaint 70 of 70 timed 3.73 s Joern — median 7.99 s, IQR 7.96 s to 8.08 s, p10–p90 7.91 s to 8.17 s, over 70 timed analyzer invocations. Warm marginal, measured separately: 6.08 s to 6.53 s per case in one process, over 2 retained repeats. Estimated per-invocation overhead, an upper bound from a trivial no-flow java fixture: 8.37 s to 9.29 s across repeats Joern 70 of 70 timed 7.99 s CodeQL — median 9.26 s, IQR 9.16 s to 9.39 s, p10–p90 9.07 s to 9.65 s, over 70 timed analyzer invocations CodeQL 70 of 70 timed 9.26 s CodeQL phase database-create — median 4.61 s, IQR 4.56 s to 4.69 s, p10–p90 4.53 s to 4.78 s. Included in this adapter's analyzer total; phase detail compares only within the adapter. database-create 4.61 s CodeQL phase database-analyze — median 4.65 s, IQR 4.57 s to 4.71 s, p10–p90 4.51 s to 4.86 s. Included in this adapter's analyzer total; phase detail compares only within the adapter. database-analyze 4.65 s Bifrost — median 9.51 s, IQR 9.38 s to 9.78 s, p10–p90 9.34 s to 10.1 s, over 70 timed analyzer invocations Bifrost 70 of 70 timed 9.51 s

The java kernel's 70 benchmark-controlled core assertions, the same no-pooling population the correctness sections read. 7 of the 8 analyzers invoked here at all; the rest do not cover this kernel and are absent from this view rather than drawn at zero. Where a row's count is below 70, the remaining assertions were declined before any subprocess was spawned and so have nothing to time.

Cold per-case analyzer-invocation wall-clock on the javascript kernel One row per analyzer, ordered fastest median first, on a logarithmic time axis. Each row draws the tenth to ninetieth percentile as a thin line, the interquartile range as a bar, and the median as a thick tick, with the median also printed as a number. Indented rows are the declared phases of adapters whose invocation exposes more than one subprocess, and belong to the adapter above them only. Materialization phases are explicitly labelled and excluded from the analyzer total. The latency page carries every value, including the minima and maxima this chart does not draw, in the data tables behind its "Show the data table" disclosures. A solid caret below a row is that adapter's separately measured warm marginal per case; a dashed span above a row is its estimated per-invocation overhead, an upper bound measured on a trivial no-flow fixture, drawn across the range its repeats spanned rather than at a point. Neither is a cold median, neither affects the ordering, and neither is subtracted from anything. 30 ms 100 ms 300 ms 1 s 3 s 10 s 30 s 100 s Wall-clock per invocation, logarithmic scale Bifrost — median 114 ms, IQR 111 ms to 123 ms, p10–p90 109 ms to 126 ms, over 70 timed analyzer invocations Bifrost 70 of 70 timed 114 ms Semgrep CE — median 810 ms, IQR 808 ms to 812 ms, p10–p90 806 ms to 824 ms, over 14 timed analyzer invocations Semgrep CE 14 of 70 timed 810 ms Joern — median 3.87 s, IQR 3.82 s to 3.91 s, p10–p90 3.79 s to 3.95 s, over 70 timed analyzer invocations Joern 70 of 70 timed 3.87 s CodeQL — median 7.30 s, IQR 7.24 s to 7.35 s, p10–p90 7.21 s to 7.43 s, over 70 timed analyzer invocations CodeQL 70 of 70 timed 7.30 s CodeQL phase database-create — median 2.97 s, IQR 2.95 s to 2.99 s, p10–p90 2.95 s to 3.01 s. Included in this adapter's analyzer total; phase detail compares only within the adapter. database-create 2.97 s CodeQL phase database-analyze — median 4.33 s, IQR 4.27 s to 4.39 s, p10–p90 4.23 s to 4.45 s. Included in this adapter's analyzer total; phase detail compares only within the adapter. database-analyze 4.33 s

The javascript kernel's 70 benchmark-controlled core assertions, the same no-pooling population the correctness sections read. 4 of the 8 analyzers invoked here at all; the rest do not cover this kernel and are absent from this view rather than drawn at zero. Where a row's count is below 70, the remaining assertions were declined before any subprocess was spawned and so have nothing to time.

Cold per-case analyzer-invocation wall-clock on the kotlin kernel One row per analyzer, ordered fastest median first, on a logarithmic time axis. Each row draws the tenth to ninetieth percentile as a thin line, the interquartile range as a bar, and the median as a thick tick, with the median also printed as a number. Indented rows are the declared phases of adapters whose invocation exposes more than one subprocess, and belong to the adapter above them only. Materialization phases are explicitly labelled and excluded from the analyzer total. The latency page carries every value, including the minima and maxima this chart does not draw, in the data tables behind its "Show the data table" disclosures. A solid caret below a row is that adapter's separately measured warm marginal per case; a dashed span above a row is its estimated per-invocation overhead, an upper bound measured on a trivial no-flow fixture, drawn across the range its repeats spanned rather than at a point. Neither is a cold median, neither affects the ordering, and neither is subtracted from anything. 30 ms 100 ms 300 ms 1 s 3 s 10 s 30 s 100 s Wall-clock per invocation, logarithmic scale Semgrep CE — median 808 ms, IQR 806 ms to 811 ms, p10–p90 803 ms to 826 ms, over 14 timed analyzer invocations. Estimated per-invocation overhead, an upper bound from a trivial no-flow kotlin fixture: 1.34 s to 1.42 s across repeats Semgrep CE 14 of 70 timed 808 ms FlowDroid — median 847 ms, IQR 831 ms to 870 ms, p10–p90 813 ms to 895 ms, over 70 timed analyzer invocations FlowDroid 70 of 70 timed 847 ms OpenTaint — median 3.91 s, IQR 3.82 s to 4.03 s, p10–p90 3.77 s to 4.07 s, over 70 timed analyzer invocations. Estimated per-invocation overhead, an upper bound from a trivial no-flow kotlin fixture: 3.76 s to 4.56 s across repeats OpenTaint 70 of 70 timed 3.91 s Bifrost — median 9.26 s, IQR 8.84 s to 9.55 s, p10–p90 8.65 s to 10.1 s, over 70 timed analyzer invocations Bifrost 70 of 70 timed 9.26 s CodeQL — median 14.6 s, IQR 14.5 s to 14.8 s, p10–p90 14.4 s to 15.0 s, over 70 timed analyzer invocations CodeQL 70 of 70 timed 14.6 s CodeQL phase database-create — median 9.88 s, IQR 9.78 s to 10.0 s, p10–p90 9.72 s to 10.2 s. Included in this adapter's analyzer total; phase detail compares only within the adapter. database-create 9.88 s CodeQL phase database-analyze — median 4.72 s, IQR 4.68 s to 4.78 s, p10–p90 4.64 s to 4.91 s. Included in this adapter's analyzer total; phase detail compares only within the adapter. database-analyze 4.72 s

The kotlin kernel's 70 benchmark-controlled core assertions, the same no-pooling population the correctness sections read. 5 of the 8 analyzers invoked here at all; the rest do not cover this kernel and are absent from this view rather than drawn at zero. Where a row's count is below 70, the remaining assertions were declined before any subprocess was spawned and so have nothing to time.

Cold per-case analyzer-invocation wall-clock on the php kernel One row per analyzer, ordered fastest median first, on a logarithmic time axis. Each row draws the tenth to ninetieth percentile as a thin line, the interquartile range as a bar, and the median as a thick tick, with the median also printed as a number. Indented rows are the declared phases of adapters whose invocation exposes more than one subprocess, and belong to the adapter above them only. Materialization phases are explicitly labelled and excluded from the analyzer total. The latency page carries every value, including the minima and maxima this chart does not draw, in the data tables behind its "Show the data table" disclosures. A solid caret below a row is that adapter's separately measured warm marginal per case; a dashed span above a row is its estimated per-invocation overhead, an upper bound measured on a trivial no-flow fixture, drawn across the range its repeats spanned rather than at a point. Neither is a cold median, neither affects the ordering, and neither is subtracted from anything. 30 ms 100 ms 300 ms 1 s 3 s 10 s 30 s 100 s Wall-clock per invocation, logarithmic scale Bifrost — median 114 ms, IQR 110 ms to 126 ms, p10–p90 108 ms to 136 ms, over 70 timed analyzer invocations Bifrost 70 of 70 timed 114 ms Semgrep CE — median 808 ms, IQR 805 ms to 811 ms, p10–p90 805 ms to 825 ms, over 14 timed analyzer invocations Semgrep CE 14 of 70 timed 808 ms Joern — median 3.75 s, IQR 3.68 s to 3.82 s, p10–p90 3.64 s to 3.86 s, over 70 timed analyzer invocations. Estimated per-invocation overhead, an upper bound from a trivial no-flow php fixture: 3.80 s to 4.20 s across repeats Joern 70 of 70 timed 3.75 s

The php kernel's 70 benchmark-controlled core assertions, the same no-pooling population the correctness sections read. 3 of the 8 analyzers invoked here at all; the rest do not cover this kernel and are absent from this view rather than drawn at zero. Where a row's count is below 70, the remaining assertions were declined before any subprocess was spawned and so have nothing to time.

Cold per-case analyzer-invocation wall-clock on the python kernel One row per analyzer, ordered fastest median first, on a logarithmic time axis. Each row draws the tenth to ninetieth percentile as a thin line, the interquartile range as a bar, and the median as a thick tick, with the median also printed as a number. Indented rows are the declared phases of adapters whose invocation exposes more than one subprocess, and belong to the adapter above them only. Materialization phases are explicitly labelled and excluded from the analyzer total. The latency page carries every value, including the minima and maxima this chart does not draw, in the data tables behind its "Show the data table" disclosures. A solid caret below a row is that adapter's separately measured warm marginal per case; a dashed span above a row is its estimated per-invocation overhead, an upper bound measured on a trivial no-flow fixture, drawn across the range its repeats spanned rather than at a point. Neither is a cold median, neither affects the ordering, and neither is subtracted from anything. 30 ms 100 ms 300 ms 1 s 3 s 10 s 30 s 100 s Wall-clock per invocation, logarithmic scale Bifrost — median 115 ms, IQR 111 ms to 125 ms, p10–p90 108 ms to 130 ms, over 70 timed analyzer invocations. Estimated per-invocation overhead, an upper bound from a trivial no-flow python fixture: 851 ms to 1.20 s across repeats Bifrost 70 of 70 timed 115 ms Semgrep CE — median 811 ms, IQR 808 ms to 821 ms, p10–p90 807 ms to 831 ms, over 14 timed analyzer invocations Semgrep CE 14 of 70 timed 811 ms Pysa — median 2.49 s, IQR 2.47 s to 2.52 s, p10–p90 2.45 s to 2.56 s, over 70 timed analyzer invocations. Estimated per-invocation overhead, an upper bound from a trivial no-flow python fixture: 2.48 s to 2.56 s across repeats Pysa 70 of 70 timed 2.49 s Joern — median 3.65 s, IQR 3.61 s to 3.70 s, p10–p90 3.56 s to 3.76 s, over 70 timed analyzer invocations Joern 70 of 70 timed 3.65 s CodeQL — median 5.19 s, IQR 5.15 s to 5.24 s, p10–p90 5.12 s to 5.30 s, over 70 timed analyzer invocations CodeQL 70 of 70 timed 5.19 s CodeQL phase database-create — median 1.94 s, IQR 1.93 s to 1.94 s, p10–p90 1.92 s to 1.97 s. Included in this adapter's analyzer total; phase detail compares only within the adapter. database-create 1.94 s CodeQL phase database-analyze — median 3.25 s, IQR 3.21 s to 3.29 s, p10–p90 3.18 s to 3.35 s. Included in this adapter's analyzer total; phase detail compares only within the adapter. database-analyze 3.25 s

The python kernel's 70 benchmark-controlled core assertions, the same no-pooling population the correctness sections read. 5 of the 8 analyzers invoked here at all; the rest do not cover this kernel and are absent from this view rather than drawn at zero. Where a row's count is below 70, the remaining assertions were declined before any subprocess was spawned and so have nothing to time.

Cold per-case analyzer-invocation wall-clock on the ruby kernel One row per analyzer, ordered fastest median first, on a logarithmic time axis. Each row draws the tenth to ninetieth percentile as a thin line, the interquartile range as a bar, and the median as a thick tick, with the median also printed as a number. Indented rows are the declared phases of adapters whose invocation exposes more than one subprocess, and belong to the adapter above them only. Materialization phases are explicitly labelled and excluded from the analyzer total. The latency page carries every value, including the minima and maxima this chart does not draw, in the data tables behind its "Show the data table" disclosures. A solid caret below a row is that adapter's separately measured warm marginal per case; a dashed span above a row is its estimated per-invocation overhead, an upper bound measured on a trivial no-flow fixture, drawn across the range its repeats spanned rather than at a point. Neither is a cold median, neither affects the ordering, and neither is subtracted from anything. 30 ms 100 ms 300 ms 1 s 3 s 10 s 30 s 100 s Wall-clock per invocation, logarithmic scale Bifrost — median 116 ms, IQR 112 ms to 123 ms, p10–p90 109 ms to 129 ms, over 70 timed analyzer invocations Bifrost 70 of 70 timed 116 ms Semgrep CE — median 817 ms, IQR 815 ms to 820 ms, p10–p90 815 ms to 825 ms, over 14 timed analyzer invocations Semgrep CE 14 of 70 timed 817 ms CodeQL — median 4.03 s, IQR 3.98 s to 4.14 s, p10–p90 3.94 s to 4.22 s, over 70 timed analyzer invocations. Estimated per-invocation overhead, an upper bound from a trivial no-flow ruby fixture: 3.78 s to 4.28 s across repeats CodeQL 70 of 70 timed 4.03 s CodeQL phase database-create — median 1.12 s, IQR 1.11 s to 1.13 s, p10–p90 1.10 s to 1.16 s. Included in this adapter's analyzer total; phase detail compares only within the adapter. database-create 1.12 s CodeQL phase database-analyze — median 2.91 s, IQR 2.87 s to 3.00 s, p10–p90 2.83 s to 3.08 s. Included in this adapter's analyzer total; phase detail compares only within the adapter. database-analyze 2.91 s Joern — median 5.26 s, IQR 5.23 s to 5.37 s, p10–p90 5.20 s to 5.44 s, over 70 timed analyzer invocations Joern 70 of 70 timed 5.26 s

The ruby kernel's 70 benchmark-controlled core assertions, the same no-pooling population the correctness sections read. 4 of the 8 analyzers invoked here at all; the rest do not cover this kernel and are absent from this view rather than drawn at zero. Where a row's count is below 70, the remaining assertions were declined before any subprocess was spawned and so have nothing to time.

Cold per-case analyzer-invocation wall-clock on the rust kernel One row per analyzer, ordered fastest median first, on a logarithmic time axis. Each row draws the tenth to ninetieth percentile as a thin line, the interquartile range as a bar, and the median as a thick tick, with the median also printed as a number. Indented rows are the declared phases of adapters whose invocation exposes more than one subprocess, and belong to the adapter above them only. Materialization phases are explicitly labelled and excluded from the analyzer total. The latency page carries every value, including the minima and maxima this chart does not draw, in the data tables behind its "Show the data table" disclosures. A solid caret below a row is that adapter's separately measured warm marginal per case; a dashed span above a row is its estimated per-invocation overhead, an upper bound measured on a trivial no-flow fixture, drawn across the range its repeats spanned rather than at a point. Neither is a cold median, neither affects the ordering, and neither is subtracted from anything. 30 ms 100 ms 300 ms 1 s 3 s 10 s 30 s 100 s Wall-clock per invocation, logarithmic scale Bifrost — median 115 ms, IQR 109 ms to 121 ms, p10–p90 108 ms to 127 ms, over 62 timed analyzer invocations Bifrost 62 of 62 timed 115 ms Semgrep CE — median 814 ms, IQR 811 ms to 816 ms, p10–p90 810 ms to 832 ms, over 14 timed analyzer invocations Semgrep CE 14 of 62 timed 814 ms Joern — median 5.11 s, IQR 5.01 s to 5.18 s, p10–p90 4.95 s to 5.26 s, over 62 timed analyzer invocations Joern 62 of 62 timed 5.11 s CodeQL — median 43.4 s, IQR 43.0 s to 44.0 s, p10–p90 42.6 s to 44.3 s, over 62 timed analyzer invocations CodeQL 62 of 62 timed 43.4 s CodeQL phase database-create — median 18.4 s, IQR 18.2 s to 18.5 s, p10–p90 18.0 s to 18.6 s. Included in this adapter's analyzer total; phase detail compares only within the adapter. database-create 18.4 s CodeQL phase database-analyze — median 25.0 s, IQR 24.7 s to 25.6 s, p10–p90 24.6 s to 26.0 s. Included in this adapter's analyzer total; phase detail compares only within the adapter. database-analyze 25.0 s

The rust kernel's 62 benchmark-controlled core assertions, the same no-pooling population the correctness sections read. 4 of the 8 analyzers invoked here at all; the rest do not cover this kernel and are absent from this view rather than drawn at zero. Where a row's count is below 62, the remaining assertions were declined before any subprocess was spawned and so have nothing to time.

Cold per-case analyzer-invocation wall-clock on the scala kernel One row per analyzer, ordered fastest median first, on a logarithmic time axis. Each row draws the tenth to ninetieth percentile as a thin line, the interquartile range as a bar, and the median as a thick tick, with the median also printed as a number. Indented rows are the declared phases of adapters whose invocation exposes more than one subprocess, and belong to the adapter above them only. Materialization phases are explicitly labelled and excluded from the analyzer total. The latency page carries every value, including the minima and maxima this chart does not draw, in the data tables behind its "Show the data table" disclosures. A solid caret below a row is that adapter's separately measured warm marginal per case; a dashed span above a row is its estimated per-invocation overhead, an upper bound measured on a trivial no-flow fixture, drawn across the range its repeats spanned rather than at a point. Neither is a cold median, neither affects the ordering, and neither is subtracted from anything. 30 ms 100 ms 300 ms 1 s 3 s 10 s 30 s 100 s Wall-clock per invocation, logarithmic scale Bifrost — median 8.72 s, IQR 8.59 s to 8.96 s, p10–p90 8.49 s to 9.59 s, over 70 timed analyzer invocations Bifrost 70 of 70 timed 8.72 s

The scala kernel's 70 benchmark-controlled core assertions, the same no-pooling population the correctness sections read. 1 of the 8 analyzers invoked here at all; the rest do not cover this kernel and are absent from this view rather than drawn at zero. Where a row's count is below 70, the remaining assertions were declined before any subprocess was spawned and so have nothing to time.

Cold per-case analyzer-invocation wall-clock on the swift kernel One row per analyzer, ordered fastest median first, on a logarithmic time axis. Each row draws the tenth to ninetieth percentile as a thin line, the interquartile range as a bar, and the median as a thick tick, with the median also printed as a number. Indented rows are the declared phases of adapters whose invocation exposes more than one subprocess, and belong to the adapter above them only. Materialization phases are explicitly labelled and excluded from the analyzer total. The latency page carries every value, including the minima and maxima this chart does not draw, in the data tables behind its "Show the data table" disclosures. A solid caret below a row is that adapter's separately measured warm marginal per case; a dashed span above a row is its estimated per-invocation overhead, an upper bound measured on a trivial no-flow fixture, drawn across the range its repeats spanned rather than at a point. Neither is a cold median, neither affects the ordering, and neither is subtracted from anything. 30 ms 100 ms 300 ms 1 s 3 s 10 s 30 s 100 s Wall-clock per invocation, logarithmic scale

The swift kernel's 66 benchmark-controlled core assertions, the same no-pooling population the correctness sections read. 0 of the 8 analyzers invoked here at all; the rest do not cover this kernel and are absent from this view rather than drawn at zero. Where a row's count is below 66, the remaining assertions were declined before any subprocess was spawned and so have nothing to time.

Cold per-case analyzer-invocation wall-clock on the typescript kernel One row per analyzer, ordered fastest median first, on a logarithmic time axis. Each row draws the tenth to ninetieth percentile as a thin line, the interquartile range as a bar, and the median as a thick tick, with the median also printed as a number. Indented rows are the declared phases of adapters whose invocation exposes more than one subprocess, and belong to the adapter above them only. Materialization phases are explicitly labelled and excluded from the analyzer total. The latency page carries every value, including the minima and maxima this chart does not draw, in the data tables behind its "Show the data table" disclosures. A solid caret below a row is that adapter's separately measured warm marginal per case; a dashed span above a row is its estimated per-invocation overhead, an upper bound measured on a trivial no-flow fixture, drawn across the range its repeats spanned rather than at a point. Neither is a cold median, neither affects the ordering, and neither is subtracted from anything. 30 ms 100 ms 300 ms 1 s 3 s 10 s 30 s 100 s Wall-clock per invocation, logarithmic scale Bifrost — median 112 ms, IQR 109 ms to 121 ms, p10–p90 107 ms to 124 ms, over 70 timed analyzer invocations Bifrost 70 of 70 timed 112 ms Semgrep CE — median 814 ms, IQR 811 ms to 816 ms, p10–p90 809 ms to 819 ms, over 14 timed analyzer invocations Semgrep CE 14 of 70 timed 814 ms CodeQL — median 7.38 s, IQR 7.32 s to 7.48 s, p10–p90 7.29 s to 7.54 s, over 70 timed analyzer invocations CodeQL 70 of 70 timed 7.38 s CodeQL phase database-create — median 3.08 s, IQR 3.07 s to 3.10 s, p10–p90 3.06 s to 3.12 s. Included in this adapter's analyzer total; phase detail compares only within the adapter. database-create 3.08 s CodeQL phase database-analyze — median 4.30 s, IQR 4.24 s to 4.37 s, p10–p90 4.21 s to 4.45 s. Included in this adapter's analyzer total; phase detail compares only within the adapter. database-analyze 4.30 s

The typescript kernel's 70 benchmark-controlled core assertions, the same no-pooling population the correctness sections read. 3 of the 8 analyzers invoked here at all; the rest do not cover this kernel and are absent from this view rather than drawn at zero. Where a row's count is below 70, the remaining assertions were declined before any subprocess was spawned and so have nothing to time.

  • median, printed beside every row
  • interquartile range (Q1–Q3)
  • p10–p90; the minimum and maximum are in the tables, not the whiskers
  • indented rows: an adapter's own declared phases
  • measured warm marginal per case — the range its retained repeats span
  • estimated per-invocation overhead (trivial fixture, upper bound), drawn across the range its repeats spanned, where that range starts at or above 25% of the row's median
How to read this figure

Every bar above is cold per-invocation wall-clock, and the warm marginal is a different quantity measured separately. Cold is what this benchmark actually runs — one process per case, start-up inside the number, because start-up is not observable from inside a single invocation. Read across runtimes, though, those bars overstate the steady-state gap: a JVM engine's row carries a JVM start a long-lived deployment pays once. So the other quantity is measured directly rather than estimated and subtracted — k cases through one tool process, for increasing k, reporting the slope of batch wall-clock against k. The carets mark it on the rows that have one, and they mark a range: the whole series is measured more than once and the figure published is the span its repeats cover, so the mark's width is its own precision rather than a number stated more exactly than it was measured. It is never subtracted from a median, never substituted for one, and never used to order the rows. Only adapters whose released CLI exposes a real multi-case batch have a figure at all; the rest are not observable with the released CLI, and the warm-marginal section records every verdict, measured and declined, with the evidence behind it.

The dashed spans are estimates, not measurements of the same kind as the bars. Each is the wall-clock of one complete adapter invocation — same pipeline, same policy, rule or query, same subprocess shape — over a trivial no-flow fixture: both benchmark endpoints declared, nothing connecting them, nothing to find. That is fixed per-invocation overhead plus the trivial fixture's own near-zero analysis, so it is an upper bound on what an adapter pays before it starts work, and it is a cold single-shot execution — the same posture the bars are measured in, and not a steady-state one. It is never subtracted from a median, never substituted for one, and never used to order the rows. The width of a span is the figure's precision, not a decoration: each measurement is repeated a fixed number of times and what is published is the range those repeats spanned — never a mean, never one chosen repeat, and never withheld for repeats that disagree, because a disagreement widens the range and that is the honest consequence of it. A span is drawn only where the range starts at or above 25% of that row's own cold median — a threshold fixed in the amendment before any estimate was measured, read at the low end so that no mark can appear on the strength of one slow repeat. Each estimate is measured in one named language, stated on the row it annotates, because boot cost is not language-free. The estimates table carries every value — every repeat behind every range, every unmarked row, and every adapter for which no estimate could be taken at all.

The axis is logarithmic. Each labelled tick is three times the one before it, so equal distances are equal ratios, not equal durations — the gap from 100 ms to 300 ms is drawn the same width as the gap from 10 s to 30 s. That is the only way this bound corpus's medians fit in one picture: they span 126 ms to 7.32 s, and on a linear axis every analyzer except the slowest would be a sliver against the origin. Because a log axis is easy to misread, every median is also printed as a number at the right of its own row.

The indented rows are phases, and they are not comparable across adapters. Only the 3 adapters whose preregistered row declares more than one subprocess have them. A phase mark sits on the same axis as the totals because it is the same kind of measurement — wall-clock of a subprocess — but reading one adapter's phase against another adapter's total is precisely the comparison the granularity rule forbids. Read a phase against the adapter it is indented under, and nothing else.

Ordering is not scoring, and this is never pooled with correctness. Rows are sorted by median because an unsorted ranking is unreadable, not because latency is a result. No correctness figure appears in this chart and no number here is blended with one: there is no combined score anywhere on this site, and a fast analyzer that answers wrongly is neither rewarded nor penalised by anything drawn above. Cases an analyzer declined before invocation are absent, not entered as zero — entering them as zero would make the analyzers that decline the most look the fastest, which is exactly backwards.

Show the data table — analyzer-invocation wall-clock per adapter, with minima and maxima
AdapterTimed invocationsMedianQ1Q3MinMax
Bifrost1031126 ms114 ms227 ms102 ms18.9 s
FlowDroid154673 ms632 ms844 ms481 ms969 ms
Semgrep CE196813 ms809 ms819 ms797 ms2.55 s
Infer2041.11 s268 ms3.23 s259 ms3.84 s
Pysa1022.49 s2.47 s2.53 s2.44 s2.95 s
OpenTaint1523.80 s3.71 s3.93 s3.51 s4.27 s
Joern4604.03 s3.75 s5.29 s3.40 s8.39 s
CodeQL8587.32 s5.19 s14.4 s3.89 s71.4 s

No mean is headlined anywhere on this page. The distributions are skewed by per-invocation fixed costs and by a small number of very long tails — the slowest single invocation in this bound latency corpus is 71.4 s, against a page-wide fastest median of 126 ms — and a mean over that shape would describe neither the typical case nor the tail. The adapters span roughly 58× between the fastest and slowest medians, which is the order-of-magnitude claim this tier is entitled to make, and the strongest one it makes.

Phase decomposition, within each adapter that has one

Section titled “Phase decomposition, within each adapter that has one”

Only the 3 adapters whose declared row has phases appear here, and each is read down its own column. These numbers exist to decompose one adapter's own cost. Racing one adapter's phase against another's total is exactly the reading the granularity rule forbids. Each adapter's numbers open from its own disclosure, one per adapter, which is also the boundary the comparison rule draws.

FlowDroid2.15.1

Show the data table — FlowDroid's 4 observable phases against its analyzer total
PhaseMedianQ1Q3MinMaxShare of the median analyzer total
total740 ms641 ms846 ms591 ms969 ms110%
compile — materialization448 ms444 ms465 ms434 ms478 msexcluded by contract
dex — materialization300 ms298 ms312 ms293 ms320 msexcluded by contract
analyze515 ms506 ms523 ms481 ms539 ms77%
analyzer total673 ms632 ms844 ms481 ms969 ms—

Inferv1.3.0

Show the data table — Infer's 2 observable phases against its analyzer total
PhaseMedianQ1Q3MinMaxShare of the median analyzer total
capture918 ms76 ms3.03 s73 ms3.64 s82%
analyze193 ms190 ms196 ms184 ms315 ms17%
analyzer total1.11 s268 ms3.23 s259 ms3.84 s—

CodeQL2.27.1

Show the data table — CodeQL's 2 observable phases against its analyzer total
PhaseMedianQ1Q3MinMaxShare of the median analyzer total
database-create3.06 s1.94 s9.67 s1.09 s32.0 s42%
database-analyze4.28 s3.30 s4.80 s2.54 s41.8 s58%
analyzer total7.32 s5.19 s14.4 s3.89 s71.4 s—

For analyzer phases, the share column is the phase median against the analyzer-total median, and the shares need not sum to 100%: a median is not additive, and the phase whose median is largest is not necessarily the phase that dominated any particular case. Materialization phases are labelled and excluded rather than made to look comparable. The analyzer-total row is the distribution of per-case contract-selected phase sums, computed per case and then summarized — not the sum of the phase medians.

Everything above is cold per-invocation wall-clock, and it stays the headline of this tier. It is also what this benchmark actually runs: one analyzer process per case, start-up inside the number, because start-up is not observable from inside a single invocation. Nothing in this section adjusts, corrects, or replaces one number above it.

But the ranked chart sets a native binary, a Python CLI, and five JVM or JVM-fronted engines on one axis, and read across runtimes those bars overstate the steady-state gap: a JVM row carries a JVM start that a long-lived deployment of the same engine pays once. The honest response is not to estimate that start-up and subtract it — that would be exactly the after-the-fact adjustment this tier's contract refuses. It is to measure the other quantity directly, which is what this section reports, under Amendment A15, preregistered before the first warm number existed.

The method, in one sentence: run k cases through one tool process for increasing k, and report the slope of batch wall-clock against k — the cost of one more case in a process that has already paid its start-up. A slope, never an average: an average per case at k still carries a 1/k share of the fixed cost, which is the very quantity the figure exists to remove. The batch reuses the cold runner's own case selection, endpoint resolution, workspace materialization, and query logic, so what is timed is the same work; only the number of cases sharing one process differs.

AdapterKernelCold median, same casesWarm marginal (least squares)Warm marginal (endpoint)BatchesRepeatsFitted fixed cost
Joernjava8.07 s6.08 s – 6.53 s6.07 s – 6.54 s k = 1, 2, 4, 8, 1621.53 s – 5.77 s
Semgrep CEjava812 ms710 ms – 732 ms664 ms – 728 ms k = 1, 2, 4, 8, 122797 ms – 1.07 s

The cold column is the median over exactly the cases the warm batch analyzed, not over the whole kernel: two figures compared across different populations would mix a population effect into a start-up effect. The two columns are never subtracted. The "fitted fixed cost" is the same fit's intercept, published as the descriptive estimate it is — it is not measured, it is not a decomposition of any cold number, and nothing on this site subtracts it from one.

What makes these figures checkable. Each batch retains the per-case evidence it produced, beside the series above. Every document the largest Joern batch produced matches that case's retained cold evidence field for field — same analyzed state, same method count, same source and sink node counts, same flow count — differing only in the scratch path each run recorded for its own workspace. A batch that had quietly analyzed less would be visible there rather than merely fast. Every figure above is a range, and that is deliberate. The whole batch series is measured more than once, every repeat is retained, and what is published is the span the repeats cover. A single slope over a handful of batches on a developer machine has a precision, and there were only two other ways to give it one — publish one run and hide the spread, which understates it, or gate publication on an agreement tolerance, which means choosing that tolerance after the numbers exist, precisely the after-the-fact decision this tier's contract refuses. The range needs neither: its width is the precision, the reader sees it directly, and nothing between the measurement and this table is discretionary. The repeats are never averaged — that would turn repeated trials into the statistic the tier's non-goals rule out — and never picked between.

These are fresh v0.9.0 series. Historical A15/A21 measurements remain on their original snapshot pages. Semgrep's original batch-16 attempt failed before analysis; its prospectively corrected largest batch is 12. No batch-16 comparison, idle-machine explanation, or causal cross-release speedup is inferred from these desktop observations.

Show the batch series — Joern on the java kernel, all 2 retained repeats
Cases in the batch (k)Run 1 — wall-clock of the one processRun 2 — wall-clock of the one processAverage per case at run 2 (not the reported figure)
19.91 s11.4 s11.4 s
215.5 s17.9 s8.93 s
427.1 s29.9 s7.48 s
849.7 s55.6 s6.95 s
16108.0 s102.6 s6.41 s
slope (least squares)6.53 s6.08 s—

The average-per-case column is shown precisely so it can be discarded: it falls with k because it is still paying a shrinking share of the fixed cost, and it would keep falling with a larger k without ever being the marginal cost. The reported figure is the slope of a wall-clock column against the first, and the published figure is the range those slopes span. Measured on Mac16,1, macos27.0.0, 10 logical CPUs — the same stamp rule as every other number here, and not comparable across machines. Machine conditions: the one-minute load average sampled immediately before each batch ranged 5.9 to 10.7 across every batch of every repeat, on a 10-core machine. The tier's hygiene rule is that no other analyzer competes for the machine while one is timed, and background load is reported rather than asserted away — so the reader can weigh the conditions instead of taking "quiet machine" on trust.

Show the batch series — Semgrep CE on the java kernel, all 2 retained repeats
Cases in the batch (k)Run 1 — wall-clock of the one processRun 2 — wall-clock of the one processAverage per case at run 2 (not the reported figure)
11.74 s1.71 s1.71 s
22.20 s2.40 s1.20 s
43.81 s3.42 s854 ms
88.34 s5.92 s741 ms
129.04 s9.72 s810 ms
slope (least squares)732 ms710 ms—

The average-per-case column is shown precisely so it can be discarded: it falls with k because it is still paying a shrinking share of the fixed cost, and it would keep falling with a larger k without ever being the marginal cost. The reported figure is the slope of a wall-clock column against the first, and the published figure is the range those slopes span. Population:one `semgrep scan` carries one --config, so a batch is restricted to cases resolving to identical rule text: 12 of the 14 invocable Java kernel assertions. Measured on Mac16,1, macos27.0.0, 10 logical CPUs — the same stamp rule as every other number here, and not comparable across machines. Machine conditions: the one-minute load average sampled immediately before each batch ranged 5.0 to 5.9 across every batch of every repeat, on a 10-core machine. The tier's hygiene rule is that no other analyzer competes for the machine while one is timed, and background load is reported rather than asserted away — so the reader can weigh the conditions instead of taking "quiet machine" on trust.

Which adapters this could be measured on, and why not the rest

Each verdict below was reached by interrogating the pinned distribution — its help output, and where that was ambiguous its own bytecode — never from a README or an assumption about the runtime. No adapter is patched, forked, or invoked outside its released interface to make a batch possible. An adapter that ships no way to analyze several cases in one process has no warm figure here, and none is estimated for it from its runtime or from another adapter's slope.

AdapterObservable with the released CLI?Evidence
JoernYes — measured A released multi-case batch that does the same per-case work, timed above.
Semgrep CEYes — measured A released multi-case batch that does the same per-case work, timed above. one `semgrep scan` carries one --config, so a batch is restricted to cases resolving to identical rule text: 12 of the 14 invocable Java kernel assertions.
FlowDroidYes in the CLI — not measured hereThe released CLI does have a batch: `-a/--apkfile` accepts a directory, and the shipped `soot-infoflow-cmd` main class lists its APKs, builds the taint wrapper once outside the loop, and iterates them in one JVM — its own help documents `-si/--skipapkfile` as "APK file to skip when processing a directory of input files", and it refuses a non-directory output with "The output file must be a directory when analyzing multiple APKs". It is not measured here because one invocation carries one `-s` sources-and-sinks definition, so a k-APK batch runs a union of k per-case endpoint configurations rather than each case's own. Whether that changes any case's result is an empirical question that has to be answered across the whole population before a marginal derived from it may be published. Named follow-up work, not a decline.
OpenTaintNot observable with the released CLIThe pinned analyzer takes one `--project` and one `--output-dir` and exits after that project. A `project.yaml` may list several `javaProjects`, but analyzing their union is one whole-program analysis over a merged call graph and a merged entry-point set — different work, not k independent case analyses — and `--semgrep-rule-set` is one rule set for the whole invocation while the benchmark resolves a rule per case. No released mode processes separate case projects in one process.
PysaNot observable with the released CLI`pyre analyze` is one-shot. The client does expose daemon commands (`start`, `incremental`, `query`), but they serve the type checker, not the taint analysis, and `analyze` never attaches to a running server. `--source-directory` is repeatable but merges directories into one project — again one whole-program analysis rather than k — and each case carries its own `.pyre_configuration`, `pyrefly.toml`, and resolved models. A daemon-shaped measurement would be stateful and not reproducibly preregisterable against this pin, so it is declined rather than attempted.
CodeQLNot observable with the released CLI`codeql database analyze` takes exactly one mandatory database, and `database create` produces exactly one database per invocation. Neither subcommand has a multi-database form in the pinned CLI.
InferNot observable with the released CLI`--results-dir` names one capture database for one project and the analyzer exits after it. Worth stating explicitly: Infer's analyzer is a native binary, and the JVM cost inside its Java row is the traced `javac` in `capture` — per-project compilation work, not process start-up a batch could amortize.
BifrostNot observable with the released CLIThe policy CLI takes one `--root` per invocation; the repeatable `--workspace NAME=PATH` is documented as requiring `--mcp` and does not reach the policy path. What can be said without measuring anything is a bound: Bifrost's cold median already includes its own process start, so its warm marginal lies between zero and that cold number. Warm figures can therefore only move the other rows down toward Bifrost's, never Bifrost's row down further — the asymmetry this amendment corrects is one the publishing vendor's engine loses by.

The decline that matters most is Bifrost's, and it is stated in the direction that costs this benchmark's publisher something. Bifrost has no warm figure here, and its cold median already includes its own process start — so whatever its warm marginal is, it lies between zero and a number already published. Adding warm figures can therefore only move the other rows down toward Bifrost's, never Bifrost's row down further. The conflation this section corrects is one the vendor's own engine benefits from, which is the reason to correct it in public rather than leave the cold chart to be read as a steady-state ranking.

These artifacts are auxiliary evidence, outside the freeze. They are retained under reports/raw/warm-latency/ — the batch series, the environment stamp, and the per-case evidence each batch produced, so that a reader can check the batch did the real work rather than less of it — but freeze/v1 does not digest them, and this amendment does not extend it to. A warm number carries the pinned amendment commit 301dd3de1463…'s immutability for its bytes and no stronger guarantee, exactly as the cold timing sidecars do. No warm run wrote a normalized report, produced an outcome, or touched a scored population; validate-reports and the freeze manifest never read this directory.

The warm marginal above answers what one more case costs a process already running, and only one adapter's released CLI let it be measured. This section answers a different question that every adapter can be asked: what does one invocation cost before it has anything to find? It is published under Amendment A24, preregistered — estimator, bias, tolerance, fixtures and presentation — before the first estimate existed.

The method, in one sentence: run one complete adapter invocation — same pipeline, same committed policy, rule or query, same flags, same subprocess shape, both subprocesses where the adapter has two — over a trivial no-flow fixture that declares the benchmark's own source and sink endpoints and never connects them, and take the runner's wall-clock around it. The fixture is generated into a scratch workspace before the process is spawned, exactly as every other fixture is, and is retained beside the measurement. Nothing is added to cases/: no population, denominator or freeze sees these files, and they are not cases.

What the number is biased by, in both directions. A trivial file is still parsed, still extracted, still queried, so the measurement is fixed overhead plus that near-zero analysis: it is an upper bound on start-up and warm-up, and the true fixed cost is at or below it. And it is a cold, single-shot execution — no warm JIT, no primed cache — which is exactly the posture the cold rows above are measured in, and exactly not the posture of a resident deployment, where both this figure and those rows would fall. It is an estimate, labelled one everywhere it appears, and it is never subtracted from a cold number, never substituted for one, and never used to order a row.

AdapterFixture languageEstimated overhead (range over every repeat)WidthEvery repeatCold median, same kernelLow end as a share of coldLoad observedMarked on the chart?
Bifrostpython851 ms – 1.20 s345 ms1.20 s, 851 ms, 879 ms115 ms740%2.6yes
CodeQLruby3.78 s – 4.28 s502 ms4.28 s, 3.78 s, 3.95 s4.03 s94%3.0 to 3.6yes
FlowDroidjava608 ms – 637 ms29 ms637 ms, 612 ms, 608 ms641 ms95%3.3 to 3.3yes
Inferc275 ms – 336 ms61 ms336 ms, 275 ms, 291 ms266 ms104%3.1yes
Joernjava8.37 s – 9.29 s924 ms8.56 s, 9.29 s, 8.37 s7.99 s105%3.6 to 3.8yes
Joernphp3.80 s – 4.20 s407 ms4.20 s, 3.84 s, 3.80 s3.75 s101%3.1 to 3.4yes
OpenTaintkotlin3.76 s – 4.56 s803 ms4.56 s, 3.76 s, 4.06 s3.91 s96%4.0 to 4.3yes
Pysapython2.48 s – 2.56 s78 ms2.56 s, 2.48 s, 2.54 s2.49 s99%4.7 to 4.8yes
Semgrep CEkotlin1.34 s – 1.42 s77 ms1.42 s, 1.34 s, 1.40 s808 ms166%4.1 to 4.2yes

The published figure is a range, and its width is the measurement's precision. Each estimate is repeated a fixed number of times — the count is a constant in the runner's source, not a per-run choice — every repeat is retained and printed above, and what is published is the range those repeats span. Never a mean, never a chosen repeat, and never withheld because the repeats disagreed: a disagreement widens the range, which is the honest consequence of it, and there is no agreement threshold anywhere in this machinery to be justified or tuned — a unit test asserts that no such constant exists. This is the same convention the warm-marginal figures publish under, shared rather than re-derived. The build re-derives each range from the retained repeats and fails if it disagrees with what the runner wrote.

The last column is what decides a chart mark, and the threshold is preregistered. A row carries a dashed span only where its range starts at or above 25% of that adapter's cold median on the same kernel. The cut is relative because the chart's axis is logarithmic and its rows span two orders of magnitude, so a share of each row's own median is the same visual claim everywhere, where a fixed millisecond cut would mark every slow adapter and no fast one whatever its overhead actually was; and it reads the range's low end so that a mark can never appear on the strength of one slow repeat. An unmarked row is not an unmeasured one — every value is in the table above.

The load column is the machine's own condition, published rather than summarized as the word "quiet". Each repeat samples the one-minute load average immediately before its subprocess is spawned, and the column is the range those samples span, so a figure taken on a busy machine can be discounted instead of taken on trust. Two disciplines stand behind those numbers: no other analyzer under measurement competes for the machine, and — because nine heavy analyzers run back to back drive the load up by themselves — each adapter's measurement waits for the load to settle before it begins, so what a row records is the machine's state and not its own position in the sequence.

Joern is the one adapter where three figures can be set side by side, and they are three different things. Its estimated per-invocation overhead on the Java kernel is 8.37 s – 9.29 s — a measured upper bound, published across the range its repeats spanned. The fitted fixed cost, the intercept of the warm batch series on the same kernel, is 1.53 s – 5.77 s — not measured at all, but inferred from a line through five batches. And its measured warm marginal, the cost of one more case in a process already running, is 6.08 s – 6.53 s. The first two are two routes to the same quantity and the third is a different quantity entirely.

Those first two routes disagree, by roughly a factor of three, and the disagreement is the informative part. Neither corrects the other and their difference is not published as a measurement, but the direction is exactly what the two constructions predict. The estimate is an upper bound that contains work the intercept excludes by construction: a whole trivial invocation — a fresh JVM, the Java front end loaded, a CPG built for the fixture, and the kernel script run over it — where the fitted intercept is what is left when a line through five batch sizes is extended back to zero cases, and every per-case cost the batch pays has been taken out of it. So the true once-per-process cost is bounded above by the estimate and approached from below by the intercept, and the gap between them is the part of "fixed cost" that neither construction can see on its own: not observable from inside one invocation, and not separable from per-case work inside a batch. That is the honest reading, and it is why this page publishes both figures rather than reconciling them into one.

Which adapters an estimate could be taken for

Each adapter is estimated in the language of its cheapest kernel arm — the core kernel whose cold median is that adapter's lowest — because that is the arm where fixed cost is the largest share of the number, and the arm whose trivial-fixture invocation is least dominated by analysis. Joern is additionally estimated on Java, so that A21's Java warm figures have a same-language estimate to be compared against; that second figure is labelled by its own language and is not the one the cheapest-arm rule selects.

AdapterFixture languageVerdictEvidence
Bifrostpython
its cheapest kernel arm
Measured3 repeats — 1.20 s, 851 ms, 879 ms — published as the 851 ms to 1.20 s range they span.
CodeQLruby
its cheapest kernel arm
Measured3 repeats — 4.28 s, 3.78 s, 3.95 s — published as the 3.78 s to 4.28 s range they span.
FlowDroidjava
its cheapest kernel arm
Measured3 repeats — 637 ms, 612 ms, 608 ms — published as the 608 ms to 637 ms range they span.
Inferc
its cheapest kernel arm
Measured3 repeats — 336 ms, 275 ms, 291 ms — published as the 275 ms to 336 ms range they span.
Joernphp
its cheapest kernel arm
Measured3 repeats — 4.20 s, 3.84 s, 3.80 s — published as the 3.80 s to 4.20 s range they span.
OpenTaintkotlin
its cheapest kernel arm
Measured3 repeats — 4.56 s, 3.76 s, 4.06 s — published as the 3.76 s to 4.56 s range they span.
Pysapython
its only kernel arm
Measured3 repeats — 2.56 s, 2.48 s, 2.54 s — published as the 2.48 s to 2.56 s range they span.
Semgrep CEkotlin
its cheapest kernel arm
Measured3 repeats — 1.42 s, 1.34 s, 1.40 s — published as the 1.34 s to 1.42 s range they span.
Joernjava
not its cheapest arm — measured so that A21's Java warm figures have a same-language estimate to be compared against
Measured3 repeats — 8.56 s, 9.29 s, 8.37 s — published as the 8.37 s to 9.29 s range they span.

There is exactly one kind of missing figure here, and it is a fact about a machine.Environment means the pinned distribution is not installed where the estimator ran, so the invocation was never attempted — explicitly not a statement about the adapter's released CLI, unlike A15's capability declines above, and resolved by running the same committed command where the distribution is installed. There is no second kind: because the figure is a range over every repeat, noisy repeats produce a wide range rather than a withheld number, so nothing is ever missing for having been measured badly. Nothing is filled in by inference either — no estimate here is derived from an adapter's runtime, its architecture, another adapter's estimate, or the same adapter's estimate in another language.

These artifacts are auxiliary evidence, outside the freeze, retained under reports/raw/invocation-overhead/: every repeat with its phase split and the one-minute load average it was taken under, the trivial fixture itself with its digest, the resolved configuration where the adapter's is per-case, the published range, and the environment stamp. freeze/v1 does not digest them, exactly as it does not digest the warm artifacts or the cold timing sidecars, so an estimate carries the pinned amendment commit 301dd3de1463…'s immutability for its bytes and no stronger guarantee. No estimator run wrote a normalized report, produced an outcome, or touched a scored population.

The tier's declared unit of aggregation: one row per slice — adapter × language × population — because a median over a whole adapter mixes languages whose fixtures differ in size and whose front ends differ in cost. Each row is one bound report. Rows are not ranked and columns are not compared across adapters with different granularity. One disclosure per adapter, for the same reason: the rows inside one of them are the rows that share a granularity.

Bifrost — 20 slices

Show the data table — 20 slices for Bifrost
SliceLanguageProfileTimed / boundMedianQ1Q3
releases/v0.9.0/normal/attempts/bifrost-smoke-attempt-01/bifrost-smoke13 languagesbenchmark-controlled117 / 118119 ms110 ms8.47 s
releases/v0.9.0/normal/attempts/bifrost-c-kernel-attempt-01/bifrost-c-kernelcbenchmark-controlled58 / 58132 ms125 ms140 ms
releases/v0.9.0/normal/attempts/bifrost-cpp-kernel-attempt-01/bifrost-cpp-kernelcppbenchmark-controlled68 / 68131 ms123 ms142 ms
releases/v0.9.0/normal/attempts/bifrost-csharp-kernel-attempt-01/bifrost-csharp-kernelcsharpbenchmark-controlled70 / 70127 ms121 ms137 ms
releases/v0.9.0/normal/attempts/bifrost-go-kernel-attempt-01/bifrost-go-kernelgobenchmark-controlled70 / 70159 ms151 ms170 ms
releases/v0.9.0/normal/attempts/bifrost-java-kernel-attempt-01/bifrost-java-kerneljavabenchmark-controlled70 / 709.51 s9.38 s9.78 s
releases/v0.9.0/normal/attempts/bifrost-java-modeling-attempt-01/bifrost-java-modelingjavabenchmark-controlled8 / 249.39 s9.35 s9.49 s
releases/v0.9.0/normal/attempts/bifrost-java-native-attempt-01/bifrost-java-nativejavatool-native0 / 12n/an/an/a
releases/v0.9.0/normal/attempts/bifrost-javascript-kernel-attempt-01/bifrost-javascript-kerneljavascriptbenchmark-controlled70 / 70114 ms111 ms123 ms
releases/v0.9.0/normal/attempts/bifrost-javascript-modeling-attempt-01/bifrost-javascript-modelingjavascriptbenchmark-controlled8 / 24121 ms119 ms123 ms
releases/v0.9.0/normal/attempts/bifrost-javascript-native-attempt-01/bifrost-javascript-nativejavascripttool-native0 / 12n/an/an/a
releases/v0.9.0/normal/attempts/bifrost-kotlin-kernel-attempt-01/bifrost-kotlin-kernelkotlinbenchmark-controlled70 / 709.26 s8.84 s9.55 s
releases/v0.9.0/normal/attempts/bifrost-php-kernel-attempt-01/bifrost-php-kernelphpbenchmark-controlled70 / 70114 ms110 ms126 ms
releases/v0.9.0/normal/attempts/bifrost-python-kernel-attempt-01/bifrost-python-kernelpythonbenchmark-controlled70 / 70115 ms111 ms125 ms
releases/v0.9.0/normal/attempts/bifrost-python-modeling-attempt-01/bifrost-python-modelingpythonbenchmark-controlled8 / 24120 ms116 ms122 ms
releases/v0.9.0/normal/attempts/bifrost-python-native-attempt-01/bifrost-python-nativepythontool-native0 / 12n/an/an/a
releases/v0.9.0/normal/attempts/bifrost-ruby-kernel-attempt-01/bifrost-ruby-kernelrubybenchmark-controlled70 / 70116 ms112 ms123 ms
releases/v0.9.0/normal/attempts/bifrost-rust-kernel-attempt-01/bifrost-rust-kernelrustbenchmark-controlled64 / 64115 ms109 ms120 ms
releases/v0.9.0/normal/attempts/bifrost-scala-kernel-attempt-01/bifrost-scala-kernelscalabenchmark-controlled70 / 708.72 s8.59 s8.96 s
releases/v0.9.0/normal/attempts/bifrost-typescript-kernel-attempt-01/bifrost-typescript-kerneltypescriptbenchmark-controlled70 / 70112 ms109 ms121 ms

FlowDroid — 4 slices

Show the data table — 4 slices for FlowDroid
SliceLanguageProfileTimed / boundMedianQ1Q3total median compile median dex median analyze median
releases/v0.9.0/normal/attempts/flowdroid-java-kernel-attempt-01/flowdroid-java-kerneljavabenchmark-controlled70 / 70641 ms627 ms667 msn/an/an/an/a
releases/v0.9.0/normal/attempts/flowdroid-java-modeling-attempt-01/flowdroid-java-modelingjavabenchmark-controlled14 / 24515 ms506 ms523 msn/a448 ms300 ms515 ms
releases/v0.9.0/normal/attempts/flowdroid-java-native-attempt-01/flowdroid-java-nativejavatool-native0 / 12n/an/an/an/an/an/an/a
releases/v0.9.0/normal/attempts/flowdroid-kotlin-kernel-attempt-01/flowdroid-kotlin-kernelkotlinbenchmark-controlled70 / 70847 ms831 ms870 msn/an/an/an/a

Semgrep CE — 17 slices

Show the data table — 17 slices for Semgrep CE
SliceLanguageProfileTimed / boundMedianQ1Q3
releases/v0.9.0/normal/attempts/semgrep-c-kernel-attempt-01/semgrep-c-kernelcbenchmark-controlled14 / 56816 ms814 ms829 ms
releases/v0.9.0/normal/attempts/semgrep-cpp-kernel-attempt-01/semgrep-cpp-kernelcppbenchmark-controlled14 / 68812 ms810 ms815 ms
releases/v0.9.0/normal/attempts/semgrep-go-kernel-attempt-01/semgrep-go-kernelgobenchmark-controlled14 / 70813 ms811 ms815 ms
releases/v0.9.0/normal/attempts/semgrep-java-kernel-attempt-01/semgrep-java-kerneljavabenchmark-controlled14 / 70812 ms809 ms813 ms
releases/v0.9.0/normal/attempts/semgrep-java-modeling-attempt-01/semgrep-java-modelingjavabenchmark-controlled10 / 24813 ms812 ms816 ms
releases/v0.9.0/normal/attempts/semgrep-java-native-attempt-01/semgrep-java-nativejavatool-native0 / 12n/an/an/a
releases/v0.9.0/normal/attempts/semgrep-javascript-kernel-attempt-01/semgrep-javascript-kerneljavascriptbenchmark-controlled14 / 70810 ms808 ms812 ms
releases/v0.9.0/normal/attempts/semgrep-javascript-modeling-attempt-01/semgrep-javascript-modelingjavascriptbenchmark-controlled10 / 24812 ms812 ms814 ms
releases/v0.9.0/normal/attempts/semgrep-javascript-native-attempt-01/semgrep-javascript-nativejavascripttool-native0 / 12n/an/an/a
releases/v0.9.0/normal/attempts/semgrep-kotlin-kernel-attempt-01/semgrep-kotlin-kernelkotlinbenchmark-controlled14 / 70808 ms806 ms811 ms
releases/v0.9.0/normal/attempts/semgrep-php-kernel-attempt-01/semgrep-php-kernelphpbenchmark-controlled14 / 70808 ms805 ms811 ms
releases/v0.9.0/normal/attempts/semgrep-python-kernel-attempt-01/semgrep-python-kernelpythonbenchmark-controlled14 / 70811 ms808 ms821 ms
releases/v0.9.0/normal/attempts/semgrep-python-modeling-attempt-01/semgrep-python-modelingpythonbenchmark-controlled10 / 24810 ms809 ms821 ms
releases/v0.9.0/normal/attempts/semgrep-python-native-attempt-01/semgrep-python-nativepythontool-native12 / 122.41 s2.38 s2.46 s
releases/v0.9.0/normal/attempts/semgrep-ruby-kernel-attempt-01/semgrep-ruby-kernelrubybenchmark-controlled14 / 70817 ms815 ms820 ms
releases/v0.9.0/normal/attempts/semgrep-rust-kernel-attempt-01/semgrep-rust-kernelrustbenchmark-controlled14 / 62814 ms811 ms816 ms
releases/v0.9.0/normal/attempts/semgrep-typescript-kernel-attempt-01/semgrep-typescript-kerneltypescriptbenchmark-controlled14 / 70814 ms811 ms816 ms

Infer — 5 slices

Show the data table — 5 slices for Infer
SliceLanguageProfileTimed / boundMedianQ1Q3capture median analyze median
releases/v0.9.0/normal/attempts/infer-c-kernel-attempt-01/infer-c-kernelcbenchmark-controlled56 / 56266 ms263 ms276 ms75 ms189 ms
releases/v0.9.0/normal/attempts/infer-cpp-kernel-attempt-01/infer-cpp-kernelcppbenchmark-controlled68 / 68274 ms269 ms1.15 s77 ms195 ms
releases/v0.9.0/normal/attempts/infer-java-kernel-attempt-01/infer-java-kerneljavabenchmark-controlled70 / 703.24 s3.22 s3.27 s3.04 s194 ms
releases/v0.9.0/normal/attempts/infer-java-modeling-attempt-01/infer-java-modelingjavabenchmark-controlled10 / 243.26 s3.24 s3.30 s3.06 s194 ms
releases/v0.9.0/normal/attempts/infer-java-native-attempt-01/infer-java-nativejavatool-native0 / 12n/an/an/an/an/a

Pysa — 3 slices

Show the data table — 3 slices for Pysa
SliceLanguageProfileTimed / boundMedianQ1Q3
releases/v0.9.0/normal/attempts/pysa-python-kernel-attempt-01/pysa-python-kernelpythonbenchmark-controlled70 / 702.49 s2.47 s2.52 s
releases/v0.9.0/normal/attempts/pysa-python-modeling-attempt-01/pysa-python-modelingpythonbenchmark-controlled20 / 242.47 s2.45 s2.50 s
releases/v0.9.0/normal/attempts/pysa-python-native-attempt-01/pysa-python-nativepythontool-native12 / 122.57 s2.56 s2.57 s

OpenTaint — 4 slices

Show the data table — 4 slices for OpenTaint
SliceLanguageProfileTimed / boundMedianQ1Q3
releases/v0.9.0/normal/attempts/opentaint-java-kernel-attempt-01/opentaint-java-kerneljavabenchmark-controlled70 / 703.73 s3.66 s3.79 s
releases/v0.9.0/normal/attempts/opentaint-java-modeling-attempt-01/opentaint-java-modelingjavabenchmark-controlled12 / 243.71 s3.65 s3.76 s
releases/v0.9.0/normal/attempts/opentaint-java-native-attempt-01/opentaint-java-nativejavatool-native0 / 12n/an/an/a
releases/v0.9.0/normal/attempts/opentaint-kotlin-kernel-attempt-01/opentaint-kotlin-kernelkotlinbenchmark-controlled70 / 703.91 s3.82 s4.03 s

Joern — 17 slices

Show the data table — 17 slices for Joern
SliceLanguageProfileTimed / boundMedianQ1Q3
releases/v0.9.0/normal/attempts/joern-java-kernel-attempt-01/joern-java-kerneljavabenchmark-controlled70 / 707.99 s7.96 s8.08 s
releases/v0.9.0/normal/attempts/joern-java-modeling-attempt-01/joern-java-modelingjavabenchmark-controlled16 / 248.03 s7.96 s8.16 s
releases/v0.9.0/normal/attempts/joern-java-native-attempt-01/joern-java-nativejavatool-native0 / 12n/an/an/a
releases/v0.9.0/normal/attempts/joern-javascript-kernel-attempt-01/joern-javascript-kerneljavascriptbenchmark-controlled70 / 703.87 s3.82 s3.91 s
releases/v0.9.0/normal/attempts/joern-javascript-modeling-attempt-01/joern-javascript-modelingjavascriptbenchmark-controlled16 / 243.91 s3.89 s3.95 s
releases/v0.9.0/normal/attempts/joern-javascript-native-attempt-01/joern-javascript-nativejavascripttool-native0 / 12n/an/an/a
releases/v0.9.0/normal/attempts/joern-php-kernel-attempt-01/joern-php-kernelphpbenchmark-controlled70 / 703.75 s3.68 s3.82 s
releases/v0.9.0/normal/attempts/joern-python-kernel-attempt-01/joern-python-kernelpythonbenchmark-controlled70 / 703.65 s3.61 s3.70 s
releases/v0.9.0/normal/attempts/joern-python-modeling-attempt-01/joern-python-modelingpythonbenchmark-controlled16 / 243.50 s3.47 s3.55 s
releases/v0.9.0/normal/attempts/joern-python-native-attempt-01/joern-python-nativepythontool-native0 / 12n/an/an/a
releases/v0.9.0/normal/attempts/joern-ruby-kernel-attempt-01/joern-ruby-kernelrubybenchmark-controlled70 / 705.26 s5.23 s5.37 s
releases/v0.9.0/normal/attempts/joern-rust-kernel-attempt-01/joern-rust-kernelrustbenchmark-controlled62 / 625.11 s5.01 s5.18 s
releases/v0.9.0/normal/attempts/joern-swift-common108-attempt-01/joern-swift-calibrationswiftbenchmark-controlled0 / 4n/an/an/a
releases/v0.9.0/normal/attempts/joern-swift-common108-attempt-01/joern-swift-coreswiftbenchmark-controlled0 / 66n/an/an/a
releases/v0.9.0/normal/attempts/joern-swift-common108-attempt-01/joern-swift-language-extensionswiftbenchmark-controlled0 / 2n/an/an/a
releases/v0.9.0/normal/attempts/joern-swift-common108-attempt-01/joern-swift-modelingswiftbenchmark-controlled0 / 24n/an/an/a
releases/v0.9.0/normal/attempts/joern-swift-common108-attempt-01/joern-swift-nativeswifttool-native0 / 12n/an/an/a

CodeQL — 22 slices

Show the data table — 22 slices for CodeQL
SliceLanguageProfileTimed / boundMedianQ1Q3database-create median database-analyze median
releases/v0.9.0/normal/attempts/codeql-c-kernel-attempt-01/codeql-c-kernelcbenchmark-controlled58 / 585.06 s5.02 s5.12 s1.21 s3.84 s
releases/v0.9.0/normal/attempts/codeql-cpp-kernel-attempt-01/codeql-cpp-kernelcppbenchmark-controlled68 / 685.11 s5.05 s13.9 s1.23 s3.89 s
releases/v0.9.0/normal/attempts/codeql-csharp-kernel-attempt-01/codeql-csharp-kernelcsharpbenchmark-controlled70 / 7018.8 s18.5 s19.2 s10.5 s8.26 s
releases/v0.9.0/normal/attempts/codeql-go-kernel-attempt-01/codeql-go-kernelgobenchmark-controlled70 / 705.73 s5.66 s5.82 s3.06 s2.67 s
releases/v0.9.0/normal/attempts/codeql-java-kernel-attempt-01/codeql-java-kerneljavabenchmark-controlled70 / 709.26 s9.16 s9.39 s4.61 s4.65 s
releases/v0.9.0/normal/attempts/codeql-java-modeling-attempt-01/codeql-java-modelingjavabenchmark-controlled24 / 248.91 s8.81 s8.99 s4.61 s4.27 s
releases/v0.9.0/normal/attempts/codeql-java-native-attempt-01/codeql-java-nativejavatool-native12 / 1213.1 s13.0 s13.2 s4.61 s8.47 s
releases/v0.9.0/normal/attempts/codeql-javascript-kernel-attempt-01/codeql-javascript-kerneljavascriptbenchmark-controlled70 / 707.30 s7.24 s7.35 s2.97 s4.33 s
releases/v0.9.0/normal/attempts/codeql-javascript-modeling-attempt-01/codeql-javascript-modelingjavascriptbenchmark-controlled24 / 247.00 s6.98 s7.05 s2.95 s4.05 s
releases/v0.9.0/normal/attempts/codeql-javascript-native-attempt-01/codeql-javascript-nativejavascripttool-native12 / 1210.8 s10.8 s10.9 s2.96 s7.86 s
releases/v0.9.0/normal/attempts/codeql-kotlin-kernel-attempt-01/codeql-kotlin-kernelkotlinbenchmark-controlled70 / 7014.6 s14.5 s14.8 s9.88 s4.72 s
releases/v0.9.0/normal/attempts/codeql-python-kernel-attempt-01/codeql-python-kernelpythonbenchmark-controlled70 / 705.19 s5.15 s5.24 s1.94 s3.25 s
releases/v0.9.0/normal/attempts/codeql-python-modeling-attempt-01/codeql-python-modelingpythonbenchmark-controlled24 / 245.00 s4.95 s5.05 s1.95 s3.05 s
releases/v0.9.0/normal/attempts/codeql-python-native-attempt-01/codeql-python-nativepythontool-native12 / 126.15 s6.12 s6.21 s1.95 s4.20 s
releases/v0.9.0/normal/attempts/codeql-ruby-kernel-attempt-01/codeql-ruby-kernelrubybenchmark-controlled70 / 704.03 s3.98 s4.14 s1.12 s2.91 s
releases/v0.9.0/normal/attempts/codeql-rust-kernel-attempt-01/codeql-rust-kernelrustbenchmark-controlled64 / 6443.4 s43.0 s44.0 s18.4 s25.0 s
releases/v0.9.0/normal/attempts/codeql-swift-common108-attempt-01/codeql-swift-calibrationswiftbenchmark-controlled0 / 4n/an/an/an/an/a
releases/v0.9.0/normal/attempts/codeql-swift-common108-attempt-01/codeql-swift-coreswiftbenchmark-controlled0 / 66n/an/an/an/an/a
releases/v0.9.0/normal/attempts/codeql-swift-common108-attempt-01/codeql-swift-language-extensionswiftbenchmark-controlled0 / 2n/an/an/an/an/a
releases/v0.9.0/normal/attempts/codeql-swift-common108-attempt-01/codeql-swift-modelingswiftbenchmark-controlled0 / 24n/an/an/an/an/a
releases/v0.9.0/normal/attempts/codeql-swift-common108-attempt-01/codeql-swift-nativeswifttool-native0 / 12n/an/an/an/an/a
releases/v0.9.0/normal/attempts/codeql-typescript-kernel-attempt-01/codeql-typescript-kerneltypescriptbenchmark-controlled70 / 707.38 s7.32 s7.48 s3.08 s4.30 s

Of the case results in this bound latency corpus, 3157 carry a retained timing sidecar. 1095 do not, and every one of them is accounted for:

Outcome of the untimed caseCases
unsupported891
inconclusive203
runner-error1

An unsupported case is decided from case metadata before the analyzer is invoked, so there is no subprocess and there is nothing to time. Those cases are absent from every distribution on this page, not entered as zero — entering them as zero would make the adapters that decline the most look the fastest, which is exactly backwards. A missing sidecar does not establish that analysis never ran; Swift reference results can retain an outcome without this timing format. Missing timings stay outside distributions. The timed and untimed columns account for the whole bound population with no remainder.

Every cold number above was derived at build time from v0.9.0's archived latency-evidence bundle. The bundle was generated from that release commit's retained timing sidecars, and its population comes from the results model generated by freeze manifest e63dcc483d30…. Snapshot pages select this bundle explicitly: advancing the current correctness freeze cannot add a case, replace a timing, or relabel an analyzer on a historical page. The warm and invocation-overhead amendments are likewise read from a checked-in auxiliary bundle pinned to commit 301dd3de146337fdda5fc409c711bf4b133e4d3c; they never read the mutable working-tree artifact directories.

One limit, stated rather than left to be assumed. The freeze/v1 manifest binds one raw-evidence digest per result; the timing sidecar and the environment stamp are additive files beside that evidence and are not themselves digested by the manifest. So a number on this page carries the freeze's guarantee that the run it belongs to is bound and byte-verified, and the release commit plus the checked-in archive's own Git history preserve the timing bytes selected for rendering. It does not carry a freeze-manifest digest over those timing bytes. Extending the manifest to bind them is a freeze/v2 question, and it is named here rather than smuggled in under a schema version that does not describe it.