Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions .github/workflows/perf.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: Java 25 Perf

on:
schedule:
- cron: '0 2 * * *'
workflow_dispatch:

jobs:
perf-linux:
runs-on: ubuntu-latest
container:
image: openjdk:25
steps:
- uses: actions/checkout@v5
- name: Run JMH perf profile
run: ./mvnw -P perf -pl quickfixj-perf-test -am verify -B -V -D"maven.javadoc.skip"="true" -D"http.keepAlive"="false" -D"maven.wagon.http.pool"="false" -D"maven.wagon.httpconnectionManager.ttlSeconds"="120"
- name: Upload JMH results
uses: actions/upload-artifact@v4
with:
name: jmh-results
path: quickfixj-perf-test/target/perf/*.json
if-no-files-found: warn

6 changes: 6 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ Builds run in parallel by default (`-T 1C` in `.mvn/maven.config`). The module g

Run `./mvnw clean package -Dmaven.javadoc.skip=true -PskipBundlePlugin,minimal-fix-latest` for the full build the CI workflows execute. Use `./mvnw clean package -DskipAT=true -PskipBundlePlugin,minimal-fix-latest` when iterating and you only need to skip the acceptance suite. Execute `./mvnw -pl quickfixj-core -am test` to focus on the core engine plus its dependencies, or append `-Dtest=SessionTest` to target a class. The examples bundle can be run after `./mvnw -pl quickfixj-examples -am package`.

For performance baselines and modernization details, see `docs/java25-modernization-plan.md`. A perf profile is available to run JMH quickly:
- Docker (Java 25): `docker run --rm -t -v "$PWD":/ws -w /ws openjdk:25 bash -lc './mvnw -P perf -pl quickfixj-perf-test -am verify'`
- Results saved to `quickfixj-perf-test/target/perf/jmh-results-<timestamp>.json`.

CI note: CI runs Linux‑only in a `openjdk:25` container until setup‑java exposes 25 for macOS/Windows.

## Coding Style & Naming Conventions
Target Java 25 (compiler release 25). Follow the existing four-space indentation and K&R brace style shown in `quickfixj-core/src/main/java/quickfix/Session.java`. Class names are CamelCase, constants SCREAMING_SNAKE_CASE, and package names all lower-case. Inject loggers with `private static final Logger LOGGER = LoggerFactory.getLogger(...)`. Keep public APIs javadoc’d and prefer immutable collections unless performance demands otherwise.

Expand Down
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,30 @@ For more information see the project website at http://www.quickfixj.org.

Check out the wiki: https://github.com/quickfix-j/quickfixj/wiki

## modernization plan (Java 25)

The roadmap to adopt modern Java 25 features (performance, concurrency, TLS hardening) is documented in `docs/java25-modernization-plan.md`.

## performance benchmarks

Use the perf Maven profile to build and run JMH quickly:

- Docker (Java 25):
```
docker run --rm -t -v "$PWD":/ws -w /ws openjdk:25 bash -lc './mvnw -P perf -pl quickfixj-perf-test -am verify'
```

- Local macOS (if JDK 25 installed):
```
JAVA_HOME=$(/usr/libexec/java_home -v 25) ./mvnw -P perf -pl quickfixj-perf-test -am verify
```

Results are saved to `quickfixj-perf-test/target/perf/jmh-results-<timestamp>.json` and also printed to stdout. See `quickfixj-perf-test/README.md` for customization options.

Baseline documentation and how-to: `docs/perf-baseline.md`.

Benchmark catalog and KPIs: `docs/perf-benchmarks.md`.

## questions
For asking questions please either use the mailing list https://lists.sourceforge.net/lists/listinfo/quickfixj-users or ask on Stack Overflow https://stackoverflow.com/questions/ask?tags=quickfixj .

Expand Down
181 changes: 181 additions & 0 deletions docs/java25-modernization-plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
# QuickFIX/J Modernization Plan for Java 25

## Executive Summary

- Objective: Modernize QuickFIX/J for Java 25 to increase throughput, reduce latency/allocations, and strengthen TLS/network defaults without breaking core APIs.
- Strategy: Apply high‑value Java 21–25 features (virtual threads, structured concurrency, pattern‑matching switch), optimize hot paths (parsing, validation, dispatch, logging, file I/O), and expand JMH coverage to guide changes.
- Delivery: Stage work in phases with clear acceptance criteria and continuous perf baselines.


## Current State & Observations

- Baseline perf (JDK 21):
- MessageParsePerfTest.parse ≈ 0.892 ops/ns
- MessageCrackerPerfTest.crack ≈ 0.099 ops/ns
- JDK 24/25 TLS: stricter defaults; legacy ciphers/keys fail (SSL tests require modern suites/keystores and endpoint ID).
- Build: Parallel reactor races fixed; CI is Linux‑only on JDK 25 (Docker). Perf profile added for one‑command JMH.


## Java 25 Features to Leverage

- Virtual Threads (since 21): scale per‑session blocking flows with low overhead.
- Structured Concurrency (21) & Scoped Values: cleaner cancellation/context propagation vs ThreadLocals.
- Pattern Matching (switch/instanceof) & switch expressions: reduce branches/boilerplate in crackers/converters.
- Modern TLS (1.3): prefer TLS_AES_* suites; enable endpoint identification by default where applicable.
- (Optional) Vector API (Incubator): SIMD‑assisted delimiter scanning in parser (behind a feature flag).


## High‑Impact Opportunities

### 1) Virtual Threads for Session Executors
- Add `SessionThreadModel` (platform | virtual) with virtual default disabled.
- Use `Executors.newVirtualThreadPerTaskExecutor()` for session/event tasks; bound pools if needed.
- Expected: better scalability for many sessions; safer blocking I/O; simplified concurrency.
- Risks: Apache MINA blocking internals; keep opt‑in, validate with AT.

### 2) Parser Hot‑Paths
- Reusable parser context (byte[]/ByteBuffer, reusable char[] scratch, per‑thread state via ThreadLocal or ScopedValue).
- Numeric fast paths (int/long/decimal) avoiding BigDecimal unless required; pre‑validated scale for price/qty.
- Avoid constructing Strings unless needed; pass slices to validation; profile hotspots.

### 3) DataDictionary Fast‑Cache
- On load, precompute immutable structures:
- tag→type (primitive array or compact map)
- tag→validator lookups
- required‑fields bitsets per msgType
- repeating group definitions cache
- Expected: fewer hash lookups/allocations on validation; stable perf under load.

### 4) Cracker Dispatch Fast Path
- Precompute handler map: (beginString or applVerId, msgType) → handler method reference/MethodHandle.
- Fallback to reflection for unknown handlers; no API break.

### 5) Logging & File I/O
- Guard debug logging (no concatenation without `isDebugEnabled()`); parameterized logging.
- Optional: queue + writer thread for bursts; batched appends; reuse encoders; consider NIO `Files.write` with pre‑sized buffers.

### 6) TLS/SSL Test & Runtime Defaults
- Update test keystores to ECDSA/RSA‑PSS; prefer TLS_AES_* suites; enable endpoint identification where expected.
- In runtime, default to TLS 1.3 + endpoint identification if unset; warn on deprecated ciphers.

### 7) Modern Java Language Features
- pattern‑matching switch/instanceof in converters/crackers
- switch expressions for compact, branch‑friendly code

### 8) JMH Coverage Expansion
- Parsing: short/long messages, repeating groups, rawData, mixed tags, dictionary on/off
- Validation: classic vs compiled dictionary
- Cracker dispatch: reflection vs precomputed handlers
- Logging/I/O: overhead with debug on/off; file writer batching

### 9) Structured Concurrency in Tests/Tools
- Use structured concurrency for coordinated tasks (tools, long ATs), to reduce flakiness and simplify cancellation.


## Benchmarks & Triage

### Baselines
- Establish Linux/JDK 25 baselines:
- Parser throughput (ns/op, ops/sec), allocation rate, GC counts
- Cracker dispatch throughput
- Validation cost with/without compiled dictionary

### Triage Rules
- Priority 1: ≥10% parser throughput gain or ≥15% allocation reduction
- Priority 2: ≥5% cracker/validation gain
- Priority 3: Logging/I/O wins that reduce tail latencies

### Perf Matrix
- Dimensions: message size (1–2KB, 5–10KB, 20–50KB), tag density, dictionary on/off, logging (off/debug), store/log (off/on)
- Outputs: ops/sec, ns/op, allocations MB/s, GC, P99 latency


## Concrete Code Changes (Sketch)

- Virtual threads
- New config; wrap session handlers with virtual‑thread executor; keep platform default
- Parser context
- `ParserState` (byte[] buffer, char[] scratch) with fast parseInt/Long/Decimal
- Avoid String creation on hot path; pass slices to validators
- DataDictionary cache
- Build immutable arrays/maps at load time; remove repeated hash lookups
- Cracker fast path
- Precompute handler map; direct call via method references/MethodHandles
- Logging & I/O
- Guard debug; parameterized logs; batch writes; reuse encoders/buffers
- TLS defaults
- Prefer TLSv1.3/TLS_AES_*; endpoint identification default; warn on insecure configs


## Risk & Compatibility

- Virtual threads: opt‑in; keep acceptance tests as authority; add bounded executor fallback
- Parser/validator: ensure numeric semantics identical; put fast numeric under config if needed (e.g., `qfj.parser.fastNumeric=true`)
- TLS: default secure, allow override; tests updated to modern suites
- Transport: Apache MINA remains default; optional new backend considered later


## CI & Tooling

- Linux‑only CI on JDK 25 (Docker image `openjdk:25`). macOS/Windows jobs are parked until setup‑java supports 25.
- Perf profile: one‑liner to build and run JMH
- Docker: `docker run --rm -t -v "$PWD":/ws -w /ws openjdk:25 bash -lc './mvnw -P perf -pl quickfixj-perf-test -am verify'`
- Local macOS (if JDK 25 installed): `JAVA_HOME=$(/usr/libexec/java_home -v 25) ./mvnw -P perf -pl quickfixj-perf-test -am verify`
- Benchmark results: saved to `quickfixj-perf-test/target/perf/jmh-results-<timestamp>.json` and printed to stdout.
- Tuning perf runs: override properties, e.g. `-Dperf.wi=5 -Dperf.i=10 -Dperf.f=2 -Dperf.tu=us -Dperf.rf=csv`.
- Optional nightly perf CI: run select benchmarks and archive `jmh-results-*.json` for trend analysis.


## Work Plan & Timeline

### Phase 0: Infra & Tests (1–2 weeks)
- Stabilize SSL tests for JDK 25 (TLS 1.3 suites, endpoint ID, keystores) — DONE
- Expand JMH coverage (parser, validation, cracker, logging/I/O) — DONE
- Add nightly perf job (optional) — DONE

### Phase 1: Quick Wins (2–3 weeks)
- DataDictionary fast‑cache tables
- Parser scratch buffers; numeric fast paths; String creation avoidance
- Logging guards; batched file I/O where safe
- Cracker dispatch precomputation (no API change)

### Phase 2: Virtual Threads & Structured Concurrency (2–4 weeks)
- Add session thread model option (virtual)
- Integrate vthreads; acceptance tests on
- Use structured concurrency in long‑running tools/tests

### Phase 3: SIMD/Vector API (Exploratory)
- Optional vectorized delimiter scanning behind feature flag; fallback path

### Phase 4: Transport Abstraction (Long‑term)
- Define SPI for alternate I/O backend; prototype modern channel transport; keep MINA default


## Acceptance Criteria

- Parser throughput +15–25% and ≥15% allocation reduction on common messages
- Cracker dispatch +5–10%
- TLS test suite green on JDK 25 with secure defaults
- Virtual threads: stable opt‑in; neutral or better perf for many sessions
- No acceptance test regressions with defaults


## Task Tracker (Initial)

- [x] Phase 0 – SSL test updates for JDK 25 (TLS 1.3 suites, endpoint ID)
- [x] Phase 0 – Add JMH: validation on/off, repeating groups, rawData; logging/I/O
- [ ] Phase 1 – DataDictionary hot caches (tag→type, validators, bitsets)
- [ ] Phase 1 – Parser context & numeric fast paths; avoid Strings on hot path
- [ ] Phase 1 – Logging guards; optional batched writer
- [ ] Phase 1 – Cracker handler map & MethodHandles
- [ ] Phase 2 – Virtual thread executor (opt‑in); bound fallback
- [ ] Phase 2 – Structured concurrency in tools/tests
- [ ] Phase 3 – Vector API parser scanning (flagged)
- [ ] Phase 4 – Transport SPI and prototype backend


## References

- JDK 21–25 features: Virtual Threads, Structured Concurrency, Pattern Matching, TLS 1.3 updates
- JMH: https://openjdk.org/projects/code-tools/jmh/
- SLF4J Logging Best Practices: parameterized logging; guards for expensive messages
Loading