| name | fory-performance-optimization |
| description | Run profile-driven bottleneck optimization across Apache Fory implementations (Java, C++, Python/Cython, Go, Rust, Swift, C#, JavaScript/TypeScript, Dart, Kotlin, Scala). Use when improving serialize/deserialize throughput or latency, recovering regressions against a reference commit, diagnosing flamegraphs, fixing perf-related CI failures, or porting proven optimizations across languages without protocol or API regressions. |
Fory Performance Optimization
Mission
Deliver measurable performance improvements in Apache Fory without protocol drift, correctness regressions, benchmark-shape tricks, or accidental API rollback.
Operating Principles
- Start from data, not intuition.
- Profile before changing hot code.
- Change one bottleneck at a time.
- Benchmark sequentially on the same machine state (one benchmark process at a time).
- Keep only measured wins or explicitly requested architecture cleanups.
- Revert speculative changes that do not pay off.
- Align with reference runtimes (usually C++ first, then Rust/Java) when behavior and ownership models differ.
Enforce Hard Constraints
- Preserve wire protocol unless explicitly requested.
- Preserve cross-language semantics and xlang compatibility.
- Never run two benchmarks at the same time on one host; run exactly one benchmark command at a time.
- Do not optimize by changing benchmark payload definitions, field encodings, or benchmark methodology.
- Do not add payload-identity or repeated-input caches that depend on benchmark shape.
- Do not restore removed APIs/legacy wrappers when the user forbids it.
- Do not preserve legacy/dead code or stale docs in optimization rounds; remove them when touched.
- Keep API surface minimal: do not add new API unless required by protocol/correctness or explicitly requested.
- Never add public hacky API for performance shortcuts; keep optimization helpers internal/private and conceptually clean.
- Do not hide regressions behind unsafe compiler flags or benchmark-only code paths.
- Keep optimization surfaces nested-safe; avoid root-only shortcuts unless they are architecturally valid and requested.
Execute Workflow
- Read context and constraints.
- Read
tasks/perf_optimization_rounds.md and tasks/lessons.md.
- Read the relevant spec in
docs/specification/ for any path that may affect wire behavior.
- Record explicit user constraints (forbidden APIs, naming, architecture, protocol rules).
- Define target and baseline.
- Identify one primary KPI (for example
Struct Serialize ns/op or ops/sec).
- Benchmark current
HEAD.
- If a reference commit is provided, benchmark it once and persist the result in a file (for example
tasks/perf_baselines/<id>.md) to avoid repeated reruns.
- Profile the hotspot.
- Capture a flamegraph or sampled stacks on the exact benchmark command.
- Quantify top costs by bucket (runtime bookkeeping, dispatch, allocation/copy, map/cache operations, buffer growth, metadata parse/validation).
- Tie each bucket to concrete file/line ownership before proposing changes.
- Form one round hypothesis.
- State one bottleneck and one expected effect.
- Prefer structural fixes over micro-tweaks.
- If another runtime already solved the same bottleneck, port its design shape first.
- Implement minimal change.
- Touch the smallest surface that can validate the hypothesis.
- Keep invariants explicit: protocol bytes, ownership, cache lifetime, reference semantics, nullability, schema-compatible behavior.
- Verify correctness.
- Run language-local build/test/lint for the touched implementation.
- Run cross-language checks when runtime/type/protocol behavior can affect xlang.
- Confirm serialized sizes and compatibility expectations where applicable.
- Benchmark and compare.
- Run targeted benchmark at least twice sequentially.
- Use longer duration when signal is noisy.
- Run one short full-suite sanity benchmark to catch collateral regressions.
- Decide keep or revert.
- Keep only if gain is repeatable or cleanup is explicitly requested and accepted with measured tradeoff.
- Revert if performance regresses or gain is within noise and complexity increases.
- If a required cleanup regresses, redesign inside the new architecture instead of restoring banned patterns.
- Log every round.
- Append one round entry to
tasks/perf_optimization_rounds.md before starting the next round.
- Include hypothesis, code change, exact commands, before/after numbers, and keep/revert decision.
- Commit retained non-trivial rounds immediately.
- Re-plan on instability.
- Stop and re-plan when benchmark runs conflict, machine contention is suspected, or profile does not match hypothesis.
- Re-ground on current
HEAD after reset/rebase/checkout events before making further changes.
Apply Decision Rules
- Treat <1-2% movement as noise unless repeated under controlled runs.
- Require explicit proof for complexity-increasing optimizations.
- Prefer deleting dead APIs and dead state quickly after refactors.
- Keep naming/API cleanup only if performance remains in band.
- Never run before/after comparisons in parallel.
Use References
Produce Output
When finishing an optimization task, report:
- Baseline command and numbers.
- Final command and numbers.
- Net delta on primary KPI.
- Correctness and compatibility verification run.
- Kept vs reverted rounds and rationale.