You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Optimizes typed ranking and decision sessions through shared package resources, bounded CUDA graph capture, cache improvements, and batched tokenization.
Changes:
Adds package-level runtime policies and multi-shape CUDA graph admission.
Introduces pinned/cost-aware caches, shared fallback encoders, and zero-copy feed views.
Adds batched directory tokenization and validation coverage.
File
Description
test/python/test_non_generative_runtime.py
Tests pinned cache entries.
test/python/test_component_session_native.py
Tests runtime policies and batch tokenization.
src/python/python.cpp
Exposes batch tokenization to Python.
src/ort_genai.h
Adds the C++ batch-tokenization wrapper.
src/ort_genai_c.h
Declares batch-tokenization C APIs.
src/models/preprocessing/genai_tokenizer.h
Declares row-wise batch encoding.
src/models/preprocessing/genai_tokenizer.cpp
Implements batched tokenizer execution.
src/models/non_generative_session.cpp
Implements cache, fallback, validation, and feed optimizations.
src/models/component_session.h
Retains shared package resources.
src/models/component_session.cpp
Implements shared resources and bounded graph capture.
🧠 Review effort: Balanced
Give feedback about Copilot approvals in this survey to enter a drawing for a $150 gift card.
reject unrepresentable size_t JSON values using the exclusive 2^N bound
replace C++20 contains, jthread, stop_token, and stop-aware waits with C++17-compatible code
hold package readiness only while a new CUDA graph signature remains capture-eligible
auto-expand initial cache capacity to hold immutable package-pinned entries
start the fallback reaper lazily after any manifest-declared safe_encoder is acquired, independent of its filename
validate fused state projections before caching
handle zero-length tokenizer rows without null pointer arithmetic
report clm_fallback_idle_ms.txt in parser errors
include BF16 in the selector type diagnostic
use the opt-in package root for the pinned-cache integration test
The public directory-tokenizer batch C/C++/Python API was removed in 542e859f, so the null-range/public-wrapper and developer-specific tokenizer-test findings are obsolete. Batch tokenization remains internal and uses the existing ORT Extensions OrtxTokenize primitive.
Validation: full Release build passed; 24 focused Python tests passed with 3 opt-in/provider skips; 9 focused native tests passed with 2 opt-in real-package skips.
Validate KEV probabilities for finite and range constraints
src/models/non_generative_session.cpp:839
The new finite/range validation is only called by the ranking path. NativeDecisionSession::Decide still accepts NaN, infinity, or out-of-range values from either KEV probability output and returns them through Answer, so the stated finite-value hardening is incomplete. Apply this validation to the assembled KEV probability vector after its count check as well.
Validate sidecar completely before pinning cache records
src/models/non_generative_session.cpp:1085
Records are pinned before the complete sidecar is validated. During InvalidateCache, a malformed later record or trailing byte throws after earlier records have already repopulated the cache, leaving the live session with projections from a file that was rejected. Parse and validate all records into temporary storage first, then pin them only after the trailing-data check succeeds.
🧠 Review effort: Balanced
Give feedback about Copilot approvals in this survey to enter a drawing for a $150 gift card.
Session timeout fails to release shared encoder reference
src/models/non_generative_session.cpp:978
With two ranking sessions sharing this encoder, each session holds one strong reference, so both reapers observe use_count() == 2 and neither ever releases its reference after the idle timeout. Reset this session's reference when its own timeout expires; any in-flight operation or other session already owns a separate shared_ptr and will keep the encoder alive.
Decision outputs lack finite and range validation
src/models/non_generative_session.cpp:2025
The decision path still returns pointer outputs without the finite/range validation added for ranking. A KEV head that emits NaN, infinity, or an out-of-range value reaches Answer, where comparisons and confidence calculations can produce an invalid typed result. Validate probabilities after either head layout has been flattened.
🧠 Review effort: Balanced
Give feedback about Copilot approvals in this survey to enter a drawing for a $150 gift card.
Validate KEV probabilities before constructing answers
src/models/non_generative_session.cpp:824
The new probability validation is only used by ranking. Both KEV head branches still pass model-produced values directly into Answer, so NaN, infinity, or values outside [0, 1] can propagate into the typed decision result and corrupt choice/confidence calculations. Apply RequireProbabilities to the extracted KEV probabilities before constructing answers as well.
Fall back to safe encoder on non-finite fused output
src/models/non_generative_session.cpp:1282
The fused fast path bypasses the new safety-encoder fallback. If the fused BF16 graph returns non-finite state projections (or probabilities), this check throws and encode_state, which is the only path that calls EnsureSafeEncoder(), is never reached. This is especially relevant when precomputed actions enable the fused path and the primary encoder was intentionally omitted at construction. Treat non-finite fused output as a failed fast path and continue through the non-fused safe-encoder path instead of returning an error.
🧠 Review effort: Balanced
Give feedback about Copilot approvals in this survey to enter a drawing for a $150 gift card.
This identity still fingerprints only hard-coded component paths, while component_manifest.json supports arbitrary filenames. The new process-wide fallback registry is keyed by this value, so replacing a manifest-mapped safe_encoder such as safe.onnx leaves the key unchanged; another live ranking session can keep returning the old encoder even after invalidation. Derive the fingerprint from the manifest's resolved component files (including external data) rather than this fixed list.
This issue also appears on line 1000 of the same file.
When two ranking sessions share this encoder, each session holds a shared_ptr, so use_count() remains at least 2 even after both are idle. Neither reaper can then release its reference, and the process retains the large fallback session until a ranking session is destroyed. The local pointer returned by EnsureSafeEncoder() already keeps an in-flight call alive, so reset this session's reference after its own idle timeout without requiring global uniqueness.
This issue also appears on line 1058 of the same file.
Validate KEV pointer outputs for finite [0,1] range
src/models/non_generative_session.cpp:2029
The new finite/range validation is applied to CLM outputs but not to either KEV pointer-head path. A pointer model returning NaN, infinity, or a value outside [0, 1] therefore reaches Round4 and is exposed in the typed decision result. Validate the assembled KEV vector before constructing answers.
🧠 Review effort: Balanced
Give feedback about Copilot approvals in this survey to enter a drawing for a $150 gift card.
The reason will be displayed to describe this comment to others. Learn more.
This looks good for approval. The change is small, reuses the existing tokenizer, and does not add public APIs. Keeping the original row lengths before padding preserves the masks and context checks. Input and output ownership also looks sound.
The two inline comments are non-blocking follow-ups: make sure the tokenizer parity tests actually run, and measure the performance gain without assuming batch tokenization is parallel.
All 67 checks passed. CPU CI reported 3,110 Python passes, including all six native component tests. The literal-tokenizer tests and real-package integration tests were skipped. I reviewed the pinned ORT Extensions implementation but did not run fresh model inference locally.
The reason will be displayed to describe this comment to others. Learn more.
Approved. The internal batch-tokenization refactor is focused, preserves ragged row handling and existing tokenizer modes, and adds no public API. The testing and performance suggestions in my COMMENT review are non-blocking follow-ups.
## Summary
Follow up on the non-blocking review feedback from #2689 by making
single-string versus batch-tokenizer parity coverage run with committed
fixtures.
## Changes
- Compare ordinary single-row and batch encoding for empty strings,
Unicode, special-token text, and mixed row lengths.
- Compare literal single-row and batch encoding with the committed
Phi-3V tokenizer for empty input, control tokens, concatenated control
tokens, Unicode, ordinary text, and unequal row lengths.
- Verify batch padding uses the configured pad token in both modes.
## Benchmark
Measured Python wrapper/call overhead with the committed Phi-3V
tokenizer and mixed text lengths (median of 9 runs):
| Batch | Per-row `encode` loop | `encode_batch` | Speedup |
|---:|---:|---:|---:|
| 1 | 3.58 us | 3.83 us | 0.93x |
| 4 | 19.65 us | 13.26 us | 1.48x |
| 16 | 101.74 us | 67.78 us | 1.50x |
| 64 | 415.51 us | 276.47 us | 1.50x |
This measures reduced Python/native wrapper and call overhead; ORT
Extensions still processes rows serially, so no parallel-tokenization
claim is made.
## Validation
- Directory tokenizer tests passed.
- Focused native non-generative tests passed.
- Python non-generative tests: 8 passed, 2 skipped.
- Ruff and diff checks passed.
Follow-up to #2689.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Optimize private CLM/KEV request tokenization without expanding the public non-generative API.
Changes
Tokenizer::EncodeBatchRowsadapter over ORT Extensions batch tokenization.EncodeBatchoverloads.API surface
No new public C, C++, or Python APIs are added. The runtime continues to expose:
OgaCreateNonGenerativeSessionOgaDestroyNonGenerativeSessionOgaNonGenerativeSessionRunJsonNonGenerativeSessionGraph capture, cache controls, action sidecars, component tensor handles, and public tokenizer APIs remain outside this change.
Validation
Dependency
Stacked on #2649.