# FIX benchmark target roster

Who libhft is benchmarked against, by language, for both the microbench and the
full bench. Reviewed 2026-09-10; reorganised by language 2026-09-29.

This is a target list, not a results table: [COVERAGE.md](COVERAGE.md) says
where each engine has got to. A missing target result must stay visible as
`not run`, `blocked` or `non-parity`, never be silently dropped from a
comparison.

The roster tables below and the status tables in COVERAGE.md are both rendered
from one file, [`engines.tsv`](engines.tsv), by `tools/gen-bench-status`. To add,
remove or re-target an engine, edit that file and rerun the tool;
`make bench-status-check` (part of `correctness`) fails if the tables and the
data drift apart, or if the Stage-2 launcher and the data disagree about which
engines it runs.

## The two populations

1. **Microbench (Stage 2 codec).** Direct encode/decode over the shared
   canonical bytes. It does not measure TCP, logon, storage, a scheduler, or an
   application's callback path. Plan: [PLAN_MICROBENCH.md](PLAN_MICROBENCH.md).
2. **Full bench (four flows, Stages 3--5).** The engine must implement all four
   exchanges: `nos_er`, `md` (ten bid/ask levels), `md_nos_er`, and
   `tick_to_trade`. A one-flow result is useful smoke evidence but not an
   end-to-end comparison row. Plan: [PLAN_FULL_BENCH.md](PLAN_FULL_BENCH.md).

## Reading the targets

- **required** -- absence is a visible work item.
- **admission candidate** -- tracked deliberately, but not promoted until its
  pinned source builds and it passes the exact shared contract (see the
  promotion rule below).
- **conditional (licensed)** -- run only from a reproducible, licensed
  installation; not required on a machine without that licence.
- **excluded** -- kept visible with its reason, but not a target.
- **parked** -- a libhft flavour set aside.
- **—** -- does not apply: a codec has no four flows, a network floor is not an
  engine. Intentional, not a gap.

libhft has six flavours, and Java and .NET each have two of them (Pure, plus a
native bridge), so those languages carry two libhft rows. `libhft-dotnet-managed`
is the .NET Pure row; the cross-engine key kept its old name in the 2026-09-24
rename. `libhft-rust-native` was parked on 2026-09-11; if it resumes it does not
substitute for `libhft-rust-pure`, nor the reverse.

Each language's **network floor** (TCP ping/pong) is the RTT before any FIX
processing. Floors are reported beside Stage 5, never as FIX engines. C++, Java,
.NET and Rust have one. The Rust closed-loop implementation lives in
`bench/rust/ping-pong`; its physical Stage 5 measurement is still pending.

## The roster, by language

<!-- BEGIN gen-bench-status:roster -->
<!-- Rendered by tools/gen-bench-status from engines.tsv and COVERAGE.csv. Do not edit by hand: change the data and rerun it. -->

### C++

| Engine | Kind | Licence | Microbench | Full bench |
|---|---|---|---|---|
| `tcp-ping-pong-cpp` | network floor | - | — | — |
| **libhft-cpp** (C++) | libhft flavour | libhft | required | required |
| `fix8` | engine | LGPL-3.0 | required | required |
| `llfix` | engine | MIT | admission candidate | required |
| `nexusfix` | engine | MIT | required | required |
| `openfix` | engine | none | excluded | excluded |
| `quickfix_cpp` | engine | QuickFIX SL 1.0 | required | required |
| `robaho_cpp_fix_engine` | engine | unknown | excluded | excluded |
| `fixpp` | codec only | MIT | required | — |
| `hffix` | codec only | BSD-2-Clause-style | required | — |
| `robaho_cpp_fix_codec` | codec only | none | excluded | — |

### C

| Engine | Kind | Licence | Microbench | Full bench |
|---|---|---|---|---|
| `libtrading` | engine | BSD-2-Clause | required | required |

### Java

| Engine | Kind | Licence | Microbench | Full bench |
|---|---|---|---|---|
| `tcp-ping-pong-java` | network floor | - | — | — |
| **libhft-java-pure** (Java Pure) | libhft flavour | libhft | required | required |
| **libhft-java-jni** (Java/JNI) | libhft flavour | libhft | required | required |
| `artio` | engine | Apache-2.0 | required | required |
| `crossfix` | engine | commercial, licensed | excluded | conditional (licensed) |
| `falcon` | engine | BSD-2-Clause | admission candidate | — |
| `philadelphia` | engine | Apache-2.0 | required | required |
| `philadelphia-fast` | engine | Apache-2.0 | — | required |
| `quickfixj` | engine | QuickFIX SL 1.0 | required | required |

### .NET

| Engine | Kind | Licence | Microbench | Full bench |
|---|---|---|---|---|
| `tcp-ping-pong-net` | network floor | - | — | — |
| **libhft-dotnet-managed** (.NET Pure) | libhft flavour | libhft | required | required |
| **libhft-dotnet-native** (.NET Native) | libhft flavour | libhft | required | required |
| `fixantenna_net` | engine | Apache-2.0 | admission candidate | admission candidate |
| `quickfix_n` | engine | QuickFIX SL 1.0 | required | required |

### Rust

| Engine | Kind | Licence | Microbench | Full bench |
|---|---|---|---|---|
| `tcp-ping-pong-rust` | network floor | - | — | — |
| **libhft-rust-pure** (Rust) | libhft flavour | libhft | required | required |
| **libhft-rust-native** (Rust over the C ABI) | libhft flavour | libhft | parked | parked |
| `dfx` | engine | MPL-2.0 | required | required |
| `fixer_rs` | engine | Apache-2.0 | required | required |
| `nanofix` | engine | MIT | required | admission candidate |
| `truefix` | engine | Apache-2.0 or MIT | required | admission candidate |
| `ferrumfix` | codec only | Apache-2.0 or MIT | required | — |

### Go

| Engine | Kind | Licence | Microbench | Full bench |
|---|---|---|---|---|
| `quickfix_go` | engine | QuickFIX SL 1.0 | required | required |
<!-- END gen-bench-status:roster -->

### Engines whose target needs a sentence

- **`crossfix`** is the only commercial engine benchmarked, and a CrossFIX row
  must name its version. The existing `S5 pass` evidence is CrossFIX 1.6
  ([CAMPAIGN_REPORT.md](CAMPAIGN_REPORT.md)); 2.0 is not headline-grade, because
  at standard capacity it logs out during warmup
  ([PLAN_JAVA_FIXBENCH.md](PLAN_JAVA_FIXBENCH.md)). It has no microbench row: its
  obfuscated buffer API cannot be reset and reparsed fairly.
- **`fixantenna_net`** (FIX Antenna .NET Core Community Edition) is the only
  candidate for a second .NET comparator: QuickFIX/n is otherwise alone, and
  non-parity on three of the four flows. EPAM/B2BITS publish it under Apache-2.0
  (`epam/fix-antenna-net-core`, targets net8.0/net48, last merge 2026-07-09), so
  unlike the rest of that vendor's line a result can be published. A Stage-2
  codec adapter is present; four-flow Stage 3 and Stage 5 evidence remain.
- **`robaho_cpp_fix_codec`** was removed from the Stage-2 launcher because it
  has no licence grant (see [Licences](#licences-checked-2026-09-29)). Its
  historical evidence remains archived with timings withheld.
- **`philadelphia-fast`** is a transport configuration of `philadelphia`, so it
  is a full-bench row but not a separate codec.

## The four-flow shape

Every promoted end-to-end engine needs both a client and server role (or two
adapters) and must pass this shape before its timings are comparable:

- `nos_er`: `35=D -> 35=8`.
- `md`: `35=V -> 35=W`, with `268=20` for ten bid/ask levels.
- `md_nos_er`: `35=V -> 35=W -> 35=D -> 35=8`.
- `tick_to_trade`: server-side `35=W -> 35=D`, with no synthetic
  ExecutionReport leg in the timed path.

## Rust decision record

The Rust set is intentionally broader than the old three-engine list.

- **Keep DFX and fixer-rs.** Both are full-session implementations with
  initiator/acceptor examples or APIs, written in Rust rather than wrapping
  another engine -- but neither is an independent design. DFX's README says it
  is "heavily derived / inspired from QuickfixN"; fixer-rs does not say so, but
  reuses 53 of QuickFIX/Go's 72 non-test source file names and QuickFIX/Go's
  README feature list verbatim. A result against them therefore measures
  QuickFIX's architecture in Rust, and should be described that way.
  DFX's upstream README still lists
  unfinished dictionary/session work, and fixer-rs describes itself as under
  heavy development, so neither earns a performance claim merely by being in
  this roster. [DFX](https://github.com/DaanA32/dfx),
  [fixer-rs](https://github.com/fixer-rs/fixer)
- **Keep FerrumFIX as codec-only.** Its own project says the engine remains
  unstable before 1.0 while providing parsing, validation and serialisation;
  that makes it a useful Stage-2 reference, not a required four-flow engine.
  [FerrumFIX](https://github.com/ferrumfix/ferrumfix)
- **Add TrueFix and NanoFIX as intake candidates.** TrueFix exposes initiator
  and acceptor support and publishes a conformance gate; NanoFIX exposes a
  zero-copy codec plus TCP client/server APIs. Both are new/small projects, so
  upstream performance claims are not evidence for this suite--our pinned
  build, contract audit and Stage-5 run decide admission.
  [TrueFix](https://github.com/truefix-labs/truefix),
  [NanoFIX](https://github.com/matthart1983/nanofix)
- **Do not add `quickfix-rs` as an independent row.** It is a Rust binding of
  QuickFIX C++, so it would measure an FFI wrapper around `quickfix_cpp`, not a
  distinct FIX engine. **Do not add `fix-rs`**: upstream states that development
  was suspended in 2017. [quickfix-rs](https://github.com/arthurlm/quickfix-rs),
  [fix-rs](https://github.com/jbendig/fix-rs)

## Exclusions and the promotion rule

`hffix`, `fixpp`, `robaho_cpp_fix_codec`, and `ferrumfix` are `N/A` for the
four-flow suite because they do not currently provide the required tested
session endpoint. `falcon` is `N/A` for all-four-flow comparison because its
message model does not cover `V`/`W` market data (it defines Logout only as a
message type; there is no session-level logout handling). `openfix`,
`robaho_cpp_fix_engine` and `robaho_cpp_fix_codec` remain discovery records,
rather than targets, until a usable licence, pinned build, and capability audit
exist.

To promote an admission candidate, pin an upstream commit/tag, record its
licence and build recipe in `candidates.tsv`, add a source-owned adapter, pass
Stage 1 and the shared extraction-sink audit, then run all four scenarios at
Stage 3 before it may enter Stage 5. A partial or non-parity row remains
diagnostic and is never used in a headline latency comparison.

**Registry intake (2026-09-29).** `candidates.tsv` now records `truefix`,
`nanofix`, `fixantenna_net`, `philadelphia` and `crossfix`, along with the
licences checked below. CrossFIX has no public source URL; its registry row
requires a licensed installation. Registration alone does not promote a row.

## Licences checked 2026-09-29

Read from the pinned clones under
`C:/Temp/libhft/open-source-fix-engines/sources`, and for FIX Antenna from a
shallow clone of upstream `main`. A licence file's presence is recorded, not a
legal opinion.

| Engine | Commit | Licence |
|---|---|---|
| `dfx` | `546c86f` | MPL-2.0 |
| `falcon` | `5ee9233` | BSD-2-Clause |
| `ferrumfix` | `3548d9f` | Apache-2.0 or MIT |
| `fixer_rs` | `c1c27c3` | Apache-2.0 |
| `truefix` | `2e1ebb9` | Apache-2.0 or MIT |
| `nanofix` | `0f79bae` | MIT |
| `fixantenna_net` | `ff38f51` | Apache-2.0 |
| `robaho_cpp_fix_codec` | `4a95e7c` | **none** -- no LICENSE, copyright or SPDX text in any of its 20 files |

`robaho_cpp_fix_codec` has the defect that made `openfix` a discovery record:
with no grant, the default is all rights reserved. Its former Stage-2 adapter
used a local compat workspace and was removed from the current launcher. The
historical result remains in the archived raw evidence, while public timing
cells stay withheld.
