-
Notifications
You must be signed in to change notification settings - Fork 1.8k
[TRTLLM-6683][feat] Support LoRA reload CPU cache evicted adapter #6786
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[TRTLLM-6683][feat] Support LoRA reload CPU cache evicted adapter #6786
Conversation
…IDIA#6510) Signed-off-by: Amit Zuker <203509407+amitz-nv@users.noreply.github.com>
📝 WalkthroughWalkthroughAdds LlmRequest::removeLoraTensors and exposes it to Python. Adjusts C++ LoRA cache checks and LoraConfig validation to allow config-only. Integrates LoRA handling in Python: propagates py_lora_path, passes lora_config to PeftCacheManager, centralizes per-request LoRA processing (load from checkpoint if needed, shape fixes, cache checks, tensor removal). Updates tests accordingly and adds dtype conversion utility. Changes
Sequence Diagram(s)sequenceDiagram
participant Worker
participant PeftCacheManager as Py PeftCacheManager
participant LoraManager
participant CppImpl as C++ Impl
participant Req as LlmRequest
Worker->>PeftCacheManager: add_request_peft(Req)
alt Req.lora_task_id is cached
PeftCacheManager->>Req: remove_lora_tensors()
else not cached
alt Req.lora_weights missing and py_lora_path present
PeftCacheManager->>LoraManager: load_from_ckpt(py_lora_path, LoraModelConfig, source)
LoraManager-->>PeftCacheManager: weights
PeftCacheManager->>Req: set lora_weights
end
PeftCacheManager->>Req: ensure shapes (unsqueeze)
end
PeftCacheManager->>CppImpl: add_request_peft(Req, True)
CppImpl-->>PeftCacheManager: task layer/module configs
PeftCacheManager-->>Worker: updated Req
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
Documentation and Community
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🔭 Outside diff range comments (7)
tensorrt_llm/executor/worker.py (1)
375-386: Fix TOCTOU race around LoRA CPU cache check; gate weights only for PyTorch backendThere’s a known race when using is_adapter_in_cpu_cache() to decide whether to omit weights (see our past learning). You compute adapter_in_cache before attempting a load, then unconditionally call _load_lora_adapter(). Re-evaluating cache status after the load reduces the window. Also, only omit weights for the PyTorch backend where a CPU peft cache is effectively shared; for TRT backend keep sending weights.
Proposed adjustment:
- adapter_in_cache = self._lora_manager.is_adapter_in_cpu_cache( - request.lora_request.adapter_id) - self._load_lora_adapter(request.lora_request) + # Load the adapter from checkpoint if missing; no-op if already loaded. + self._load_lora_adapter(request.lora_request) + # Check cache status after the load to minimize TOCTOU race. + adapter_in_cache = self._lora_manager.is_adapter_in_cpu_cache( + request.lora_request.adapter_id) uid = str(request.lora_request.adapter_id) - lora_config = tllm.LoraConfig( - task_id=request.lora_request.adapter_id, - weights=self._lora_manager.cpp_lora_weights[uid] - if not adapter_in_cache else None, - config=self._lora_manager.cpp_lora_config[uid]) + # For TRT backend, always send weights. For PyTorch backend, omit weights if present in CPU cache. + send_weights = (not self._is_pytorch_backend) or (not adapter_in_cache) + lora_config = tllm.LoraConfig( + task_id=request.lora_request.adapter_id, + weights=self._lora_manager.cpp_lora_weights[uid] if send_weights else None, + config=self._lora_manager.cpp_lora_config[uid]) py_lora_path = request.lora_request.lora_pathThis keeps TRT safe and narrows the race for PyTorch.
tensorrt_llm/_torch/pyexecutor/_util.py (1)
503-509: PeftCacheManager signature validated; lora_config propagation is correct.Nit: duplicate
ModelConfigimports intensorrt_llm/_torch/pyexecutor/_util.py—remove or alias one to avoid ambiguity.
- Line 10:
from tensorrt_llm._torch.model_config import ModelConfig- Line 21:
from ..model_config import ModelConfigcpp/tensorrt_llm/pybind/batch_manager/bindings.cpp (1)
97-97: Build breaker: py::classh should be py::class_.These occurrences will fail to compile. Replace with py::class_.
- py::classh<GenLlmReq>(m, "GenericLlmRequest") + py::class_<GenLlmReq>(m, "GenericLlmRequest") ... - py::classh<tb::LlmRequest, GenLlmReq>(m, "LlmRequest", pybind11::dynamic_attr()) + py::class_<tb::LlmRequest, GenLlmReq>(m, "LlmRequest", pybind11::dynamic_attr())Also applies to: 258-258
cpp/tests/unit_tests/executor/loraConfigTest.cpp (1)
117-117: Test bug: writing to the wrong buffer (weightsData instead of configData).This invalidates the serialization test for config.
- weightsData[i * configDim1 + j] = 3 * (i * configDim1 + j); + configData[i * configDim1 + j] = 3 * (i * configDim1 + j);cpp/tensorrt_llm/batch_manager/peftCacheManager.cpp (1)
338-342: Prefer using LlmRequest::removeLoraTensors to clear LoRA data.Simplify and centralize cleanup with the new API.
- // free memory associated with lora weights in llmRequest - req->clearLoraWeights(); - req->clearLoraConfig(); + // free memory associated with LoRA tensors in the request + req->removeLoraTensors();tests/unittest/_torch/test_resource_manager.py (1)
244-249: Enforce LoRA config dtype to int32 when converting from NumPyThe executor expects LoRA config to be INT32. Make the dtype explicit to avoid accidental int64 loads on some platforms.
- if lora_config is not None: - lora_config = torch.from_numpy(lora_config) + if lora_config is not None: + lora_config = torch.from_numpy(lora_config).to(torch.int32)tensorrt_llm/_torch/pyexecutor/resource_manager.py (1)
1237-1242: Fix ensure_batch return annotationTests treat ensure_batch’s return as a mapping (request_id -> peft table). The annotation says List[LlmRequest], which is misleading.
- def ensure_batch(self, + def ensure_batch(self, context_batch: List[LlmRequest], generation_batch: List[LlmRequest], - reset_gpu_cache: bool = False) -> List[LlmRequest]: + reset_gpu_cache: bool = False) -> dict: return self.impl.ensure_batch(context_batch, generation_batch, reset_gpu_cache)If there’s a concrete type for the peft table entries, prefer annotating that mapping precisely.
🧹 Nitpick comments (11)
tensorrt_llm/_torch/pyexecutor/llm_request.py (1)
288-289: Carry py_lora_path in LlmRequest: LGTM; please document the new fieldThe addition is consistent with other py_* attributes and child-copying behavior. Please add a short docstring/comment in the constructor documenting py_lora_path semantics (expected to be an on-disk LoRA ckpt path, optional), per guidelines.
tensorrt_llm/_utils.py (1)
198-202: Type annotate and document binding_to_str_dtypeMinor polishing for clarity and Sphinx docs.
-def binding_to_str_dtype(binding_dtype) -> str: - ret = _binding_to_str_dtype.get(binding_dtype) +def binding_to_str_dtype(binding_dtype: DataType) -> str: + """Return the string dtype name corresponding to a bindings DataType.""" + ret = _binding_to_str_dtype.get(binding_dtype) assert ret is not None, f'Unsupported binding dtype: {binding_dtype}' return retcpp/include/tensorrt_llm/batch_manager/llmRequest.h (1)
2351-2353: Prefer Doxygen-style comment for new public APIUse Doxygen single-line comments for headers to match project guidelines.
- // Remove LoRA weights and LoRA config tensors - void removeLoraTensors(); + //! Remove LoRA weights and LoRA config tensors. + void removeLoraTensors();tensorrt_llm/_torch/pyexecutor/_util.py (1)
8-13: Duplicate ModelConfig imports.ModelConfig is imported twice from different paths; keep a single authoritative import to avoid ambiguity.
-from tensorrt_llm._torch.model_config import ModelConfig -... -from ..model_config import ModelConfig +# Prefer one consistent import; for example: +from tensorrt_llm._torch.model_config import ModelConfigAlso applies to: 21-24
cpp/tests/unit_tests/executor/loraConfigTest.cpp (1)
88-90: Misleading comment (optional).Comment says "Wrong memory type" but this case checks mismatched shapes. Update for clarity.
- // Wrong memory type + // Shapes not matchingcpp/tensorrt_llm/executor/loraConfig.cpp (1)
41-56: Tighten weights branch usability: small refactor and message polish
- Consider binding value() once to make intent and safety clearer and avoid repeated value() calls.
- Error messages mix “lora”/“LoRA”. Prefer consistent “LoRA” for clarity.
- Optional: if there’s a required dtype for weights (e.g., FP16/FP32), validate it here; otherwise, please confirm weights dtype is intentionally unconstrained.
Apply this minimal refactor for readability and consistency:
- if (mWeights.has_value()) + if (mWeights.has_value()) { - SizeType32 constexpr expectedWeightsDims = 2; - TLLM_CHECK_WITH_INFO( - mConfig.has_value(), "Request for LoRA inference with lora weights must also have lora config"); + SizeType32 constexpr expectedWeightsDims = 2; + TLLM_CHECK_WITH_INFO( + mConfig.has_value(), "Request for LoRA inference with LoRA weights must also have LoRA config"); - TLLM_CHECK_WITH_INFO( - mWeights.value().getShape().size() == expectedWeightsDims, "Expected weights tensor to have 2 dimensions"); + auto const& config = mConfig.value(); + auto const& weights = mWeights.value(); + TLLM_CHECK_WITH_INFO( + weights.getShape().size() == expectedWeightsDims, "Expected weights tensor to have 2 dimensions"); - TLLM_CHECK_WITH_INFO(mWeights.value().getMemoryType() != MemoryType::kGPU - && mWeights.value().getMemoryType() != MemoryType::kUNKNOWN, - "Expected lora weights to be in CPU memory"); + TLLM_CHECK_WITH_INFO(weights.getMemoryType() != MemoryType::kGPU + && weights.getMemoryType() != MemoryType::kUNKNOWN, + "Expected LoRA weights to be in CPU memory"); - TLLM_CHECK_WITH_INFO(mConfig.value().getShape()[0] == mWeights.value().getShape()[0], - "Expected dim 0 of lora weights and lora config to have the same size"); + TLLM_CHECK_WITH_INFO(config.getShape()[0] == weights.getShape()[0], + "Expected dim 0 of LoRA weights and LoRA config to have the same size"); }tests/unittest/_torch/test_resource_manager.py (3)
12-12: Follow Python import namespace guidelinePer project guidelines: always maintain the namespace when importing in Python. Prefer importing the module, then referencing the class.
-from tensorrt_llm._torch.pyexecutor.llm_request import LlmRequest +from tensorrt_llm._torch.pyexecutor import llm_request as py_llm_requestAnd below (Line 251), construct via:
- request = LlmRequest( + request = py_llm_request.LlmRequest(
251-260: Consider adding py_lora_path coverageYou now support lazy loading via py_lora_path. To exercise that path, add a test variant that sets request.py_lora_path and omits lora_weights, ensuring add_request_peft loads from checkpoint as intended.
I can draft such a test if helpful.
265-269: Docstring nit: clarify expected shapes and dtypesAdd that weights are float16 and config is int32, both 2D, to make expectations explicit.
- """Create mock LoRA weights and config. + """Create mock LoRA weights and config. + + Weights: float16, 2D CPU tensor. + Config: int32, 2D CPU tensor.tests/unittest/llmapi/test_llm.py (1)
1462-1480: Avoid duplicating the helper across test suitesThis helper mirrors logic added in the PyTorch test file. Consider centralizing it in tests/unittest/llmapi/lora_test_utils.py and reusing from both places to reduce drift.
tests/unittest/llmapi/test_llm_pytorch.py (1)
314-333: Mirror helper exists in TRT tests—consider centralizingThis helper duplicates the variant in tests/unittest/llmapi/test_llm.py. To reduce drift, move it to llmapi/lora_test_utils.py and import from both test files.
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (16)
cpp/include/tensorrt_llm/batch_manager/llmRequest.h(1 hunks)cpp/tensorrt_llm/batch_manager/llmRequest.cpp(1 hunks)cpp/tensorrt_llm/batch_manager/peftCacheManager.cpp(1 hunks)cpp/tensorrt_llm/executor/loraConfig.cpp(1 hunks)cpp/tensorrt_llm/nanobind/batch_manager/bindings.cpp(1 hunks)cpp/tensorrt_llm/pybind/batch_manager/bindings.cpp(1 hunks)cpp/tests/unit_tests/executor/loraConfigTest.cpp(1 hunks)tensorrt_llm/_torch/pyexecutor/_util.py(1 hunks)tensorrt_llm/_torch/pyexecutor/llm_request.py(2 hunks)tensorrt_llm/_torch/pyexecutor/resource_manager.py(4 hunks)tensorrt_llm/_utils.py(2 hunks)tensorrt_llm/executor/worker.py(3 hunks)tests/unittest/_torch/test_resource_manager.py(7 hunks)tests/unittest/llmapi/test_llm.py(2 hunks)tests/unittest/llmapi/test_llm_pytorch.py(4 hunks)tests/unittest/utils/util.py(1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{cpp,h,hpp,cc,cxx}
📄 CodeRabbit Inference Engine (CODING_GUIDELINES.md)
**/*.{cpp,h,hpp,cc,cxx}: Closing braces of namespaces should have a comment saying the namespace it closes (e.g., } // namespace foo).
Prefer const or constexpr variables over #defines whenever possible.
A variable that is not modified after its initialization should be declared as const.
Except 0 (used for checking signness/existence/emptiness), nullptr, true, false, all other literals should only be used for variable initialization.
Use the Allman indentation style for braces in C++ code.
Put the semicolon for an empty for or while loop in a new line.
The statement forming the body of a switch, while, do..while, or for statement shall be a compound statement (use brace-delimited statements).
If and else should always be followed by brace-delimited statements, even if empty or a single statement.
C++ filenames should use camel case with the first letter lowercase (e.g., thisIsAFilename.cpp), and all files involved in a compilation target must have case-insensitive unique filenames.
All types (including class names) should use camel case with uppercase first letter (e.g., FooBarClass).
Local variables, methods, and namespaces should use camel case with first letter lowercase (e.g., localFooBar).
Non-magic-number global variables that are non-static and not defined in anonymous namespace should use camel case prefixed by 'g' (e.g., gDontUseGlobalFoos).
Non-magic-number global variables that are static or defined in an anonymous namespace should use camel case prefixed by 's' (e.g., sMutableStaticGlobal).
Locally visible static variables should use camel case with lowercase prefix 's' as the first letter (e.g., static std::once_flag sFlag;).
Class member variables should use camel case prefixed with 'm' (e.g., mNbFooValues). Public member variables do not require the 'm' prefix but it is encouraged for clarity.
Enumerations, global constants, static constants at class-scope, and function-scope magic-number/literal constants should be uppercase snake case with prefix...
Files:
cpp/tensorrt_llm/batch_manager/llmRequest.cppcpp/include/tensorrt_llm/batch_manager/llmRequest.hcpp/tensorrt_llm/batch_manager/peftCacheManager.cppcpp/tests/unit_tests/executor/loraConfigTest.cppcpp/tensorrt_llm/nanobind/batch_manager/bindings.cppcpp/tensorrt_llm/pybind/batch_manager/bindings.cppcpp/tensorrt_llm/executor/loraConfig.cpp
**/*.{cpp,h,hpp,cc,cxx,cu,py}
📄 CodeRabbit Inference Engine (CODING_GUIDELINES.md)
All TensorRT-LLM Open Source Software code should contain an NVIDIA copyright header that includes the current year. This includes .cpp, .h, .cu, .py, and any other source files which are compiled or interpreted.
Files:
cpp/tensorrt_llm/batch_manager/llmRequest.cppcpp/include/tensorrt_llm/batch_manager/llmRequest.htensorrt_llm/_utils.pycpp/tensorrt_llm/batch_manager/peftCacheManager.cppcpp/tests/unit_tests/executor/loraConfigTest.cppcpp/tensorrt_llm/nanobind/batch_manager/bindings.cpptensorrt_llm/_torch/pyexecutor/llm_request.pycpp/tensorrt_llm/pybind/batch_manager/bindings.cpptensorrt_llm/_torch/pyexecutor/_util.pytests/unittest/utils/util.pytests/unittest/_torch/test_resource_manager.pycpp/tensorrt_llm/executor/loraConfig.cpptensorrt_llm/executor/worker.pytests/unittest/llmapi/test_llm.pytensorrt_llm/_torch/pyexecutor/resource_manager.pytests/unittest/llmapi/test_llm_pytorch.py
**/*.{h,hpp}
📄 CodeRabbit Inference Engine (CODING_GUIDELINES.md)
Use a preprocessor guard in header files. The guard name must have prefix TRTLLM_ followed by the filename, all in caps, and no trailing underscore.
Files:
cpp/include/tensorrt_llm/batch_manager/llmRequest.h
**/*.py
📄 CodeRabbit Inference Engine (CODING_GUIDELINES.md)
**/*.py: Python code should conform to Python 3.8+.
Indent Python code with 4 spaces. Do not use tabs.
Always maintain the namespace when importing in Python, even if only one class or function from a module is used.
Python filenames should use snake_case (e.g., some_file.py).
Python classes should use PascalCase (e.g., class SomeClass).
Python functions and methods should use snake_case (e.g., def my_awesome_function():).
Python local variables should use snake_case. Prefix k for variable names that start with a number (e.g., k_99th_percentile).
Python global variables should use upper snake_case and prefix G (e.g., G_MY_GLOBAL).
Python constants should use upper snake_case (e.g., MY_CONSTANT).
Avoid shadowing variables declared in an outer scope in Python.
Initialize all externally visible members of a Python class in the constructor.
For interfaces that may be used outside a Python file, prefer docstrings over comments.
Comments in Python should be reserved for code within a function, or interfaces that are local to a file.
Use Google style docstrings for Python classes and functions, which can be parsed by Sphinx.
Attributes and variables in Python can be documented inline; attribute docstrings will be rendered under the class docstring.
Avoid using reflection in Python when functionality can be easily achieved without it.
When using try-except blocks in Python, limit the except to the smallest set of errors possible.
When using try-except blocks to handle multiple possible variable types in Python, keep the body of the try as small as possible, using the else block to implement the logic.
Files:
tensorrt_llm/_utils.pytensorrt_llm/_torch/pyexecutor/llm_request.pytensorrt_llm/_torch/pyexecutor/_util.pytests/unittest/utils/util.pytests/unittest/_torch/test_resource_manager.pytensorrt_llm/executor/worker.pytests/unittest/llmapi/test_llm.pytensorrt_llm/_torch/pyexecutor/resource_manager.pytests/unittest/llmapi/test_llm_pytorch.py
🧠 Learnings (3)
📓 Common learnings
Learnt from: amitz-nv
PR: NVIDIA/TensorRT-LLM#5616
File: tensorrt_llm/executor/worker.py:375-384
Timestamp: 2025-07-17T09:01:27.402Z
Learning: In tensorrt_llm/executor/worker.py, the LoRA adapter cache optimization logic that checks `is_adapter_in_cpu_cache()` and conditionally passes None for weights/config has a known race condition issue that cannot be solved with simple error handling or verification checks. This is a known limitation that requires a more comprehensive solution.
📚 Learning: 2025-07-28T17:06:08.621Z
Learnt from: moraxu
PR: NVIDIA/TensorRT-LLM#6303
File: tests/integration/test_lists/qa/examples_test_list.txt:494-494
Timestamp: 2025-07-28T17:06:08.621Z
Learning: In TensorRT-LLM testing, it's common to have both CLI flow tests (test_cli_flow.py) and PyTorch API tests (test_llm_api_pytorch.py) for the same model. These serve different purposes: CLI flow tests validate the traditional command-line workflow, while PyTorch API tests validate the newer LLM API backend. Both are legitimate and should coexist.
Applied to files:
tests/unittest/_torch/test_resource_manager.py
📚 Learning: 2025-07-17T09:01:27.402Z
Learnt from: amitz-nv
PR: NVIDIA/TensorRT-LLM#5616
File: tensorrt_llm/executor/worker.py:375-384
Timestamp: 2025-07-17T09:01:27.402Z
Learning: In tensorrt_llm/executor/worker.py, the LoRA adapter cache optimization logic that checks `is_adapter_in_cpu_cache()` and conditionally passes None for weights/config has a known race condition issue that cannot be solved with simple error handling or verification checks. This is a known limitation that requires a more comprehensive solution.
Applied to files:
tensorrt_llm/executor/worker.pytensorrt_llm/_torch/pyexecutor/resource_manager.py
🧬 Code Graph Analysis (11)
cpp/include/tensorrt_llm/batch_manager/llmRequest.h (1)
cpp/tensorrt_llm/batch_manager/llmRequest.cpp (2)
removeLoraTensors(368-372)removeLoraTensors(368-368)
cpp/tensorrt_llm/batch_manager/peftCacheManager.cpp (1)
cpp/tensorrt_llm/pybind/batch_manager/kvCacheManager.cpp (6)
llmRequest(169-172)llmRequest(169-169)llmRequest(245-248)llmRequest(245-245)llmRequest(277-280)llmRequest(277-277)
cpp/tests/unit_tests/executor/loraConfigTest.cpp (1)
cpp/tensorrt_llm/executor/loraConfig.cpp (1)
LoraConfig(25-57)
cpp/tensorrt_llm/nanobind/batch_manager/bindings.cpp (2)
cpp/include/tensorrt_llm/batch_manager/llmRequest.h (1)
updatePerfMetrics(1807-1824)cpp/tensorrt_llm/batch_manager/llmRequest.cpp (2)
removeLoraTensors(368-372)removeLoraTensors(368-368)
cpp/tensorrt_llm/pybind/batch_manager/bindings.cpp (2)
cpp/include/tensorrt_llm/batch_manager/llmRequest.h (1)
updatePerfMetrics(1807-1824)cpp/tensorrt_llm/batch_manager/llmRequest.cpp (2)
removeLoraTensors(368-372)removeLoraTensors(368-368)
tensorrt_llm/_torch/pyexecutor/_util.py (1)
tensorrt_llm/_torch/models/modeling_phi4mm.py (1)
lora_config(244-264)
tests/unittest/_torch/test_resource_manager.py (4)
tensorrt_llm/_torch/pyexecutor/llm_request.py (1)
LlmRequest(265-396)tensorrt_llm/_torch/pyexecutor/resource_manager.py (1)
PeftCacheManager(1170-1275)tensorrt_llm/llmapi/llm_args.py (1)
PeftCacheConfig(791-857)tensorrt_llm/lora_manager.py (1)
LoraConfig(236-252)
tensorrt_llm/executor/worker.py (2)
tensorrt_llm/lora_manager.py (1)
cpp_lora_config(1222-1223)tensorrt_llm/_torch/models/modeling_phi4mm.py (1)
lora_request(267-288)
tests/unittest/llmapi/test_llm.py (1)
tests/unittest/llmapi/test_llm_pytorch.py (4)
test_llama_7b_multi_lora_evict_and_reload_lora_gpu_cache(336-345)_check_llama_7b_multi_lora_evict_load_new_adapters(314-332)test_llama_7b_multi_lora_evict_and_load_new_adapters_in_cpu_and_gpu_cache(349-358)test_llama_7b_multi_lora_read_from_cache_after_insert(362-369)
tensorrt_llm/_torch/pyexecutor/resource_manager.py (4)
tensorrt_llm/lora_manager.py (5)
LoraConfig(236-252)LoraManager(682-1297)LoraModelConfig(256-260)lora_weights(1210-1211)load_from_ckpt(785-817)tensorrt_llm/_utils.py (2)
binding_to_str_dtype(198-201)nvtx_range(842-861)tensorrt_llm/_torch/pyexecutor/llm_request.py (1)
LlmRequest(265-396)tensorrt_llm/executor/request.py (1)
ckpt_source(52-53)
tests/unittest/llmapi/test_llm_pytorch.py (1)
tests/unittest/llmapi/test_llm.py (4)
_check_llama_7b_multi_lora_evict_load_new_adapters(1461-1479)test_llama_7b_multi_lora_evict_and_reload_lora_gpu_cache(1483-1492)test_llama_7b_multi_lora_evict_and_load_new_adapters_in_cpu_and_gpu_cache(1496-1505)test_llama_7b_multi_lora_read_from_cache_after_insert(1509-1516)
🔇 Additional comments (17)
tensorrt_llm/_torch/pyexecutor/llm_request.py (1)
494-495: Propagate py_lora_path from executor_request: LGTMSafe getattr with default None. Matches the new LlmRequest field and the PyTorch backend flow.
tests/unittest/utils/util.py (1)
6-6: Typing import refinement: LGTMImporting Any, Generator is correct and used below.
tensorrt_llm/executor/worker.py (1)
501-501: Attach py_lora_path to the backend request: LGTMForwarding the path enables on-demand adapter loading in the PyTorch backend.
cpp/tensorrt_llm/nanobind/batch_manager/bindings.cpp (1)
378-380: Nanobind exposure looks correct.Binding names and targets match C++ methods; consistent with pybind naming.
cpp/tensorrt_llm/pybind/batch_manager/bindings.cpp (1)
384-386: Expose remove_lora_tensors via pybind — LGTM.Naming and binding are consistent with nanobind and C++.
cpp/tests/unit_tests/executor/loraConfigTest.cpp (1)
56-58: Updated validation semantics — looks correct.Allowing config-only and disallowing weights-only with the clearer message aligns with implementation changes.
Also applies to: 60-62
cpp/tensorrt_llm/batch_manager/peftCacheManager.cpp (1)
596-605: determineNumPages fallback safe — no callers expect a custom exception
- All calls to
determineNumPages(req)propagatestd::runtime_errorwithout catching a specialized exception:
- cpp/tensorrt_llm/batch_manager/capacityScheduler.cpp (lines 260 & 306)
- cpp/tensorrt_llm/runtime/loraCache.cpp (lines 187, 443 & 911)
- Python/Nanobind bindings in kvCacheManager.cpp
- Unit tests in cpp/tests/unit_tests/runtime/loraCacheTest.cpp
- No occurrences of
PeftTaskNotCachedExceptionin the repository.LlmRequest::removeLoraTensors()is defined, but there are no manual LORA–tensor resets elsewhere to replace.No further changes required here.
cpp/tensorrt_llm/executor/loraConfig.cpp (1)
30-40: Config-only path validation looks correct; allowing config-only is a good changeYou preserved CPU-memory and INT32 dtype checks and ensured 2D config; clean and consistent with downstream expectations. No functional issues found here.
tests/unittest/_torch/test_resource_manager.py (1)
277-282: Passing an empty LoraConfig() is acceptable here but can be brittlePeftCacheManager builds a LoraModelConfig from lora_config. While you don't trigger load-from-ckpt in these tests, providing explicit lora_target_modules matching the mocked model can future-proof this test if py_lora_path coverage is added later.
Would you prefer I update these call sites to LoraConfig(lora_target_modules=['attn_q','attn_k','attn_v']) to align with the mock model?
Also applies to: 291-295, 325-329, 359-363
tests/unittest/llmapi/test_llm.py (1)
1483-1518: New LoRA eviction/reload tests: LGTMGood coverage across GPU-only eviction/reload, CPU+GPU cache interplay, and cache read-after-insert. The @skip_gpu_memory_less_than_40gb gating is appropriate.
tensorrt_llm/_torch/pyexecutor/resource_manager.py (4)
1172-1176: Constructor signature updated: document and validate callsitesAdding lora_config is a good move. Please update the class docstring to document the new parameter and ensure all callsites are passing a LoraConfig (tests seem updated).
1205-1211: LoraModelConfig construction: LGTMUsing binding_to_str_dtype(model_config.data_type) keeps the dtype consistent with backend expectations.
1254-1267: Centralizing LoRA handling in prepare_resources: LGTMUsing add_request_peft for each context request and then propagating py_lora_task_layer_module_configs keeps the flow consistent.
1213-1235: LoRA cache race can’t be fixed with simple try/catch
The known TOCTOU in the LoRA adapter cache (tensorrt_llm/executor/worker.py around lines 375–384) cannot be addressed via catch-and-retry or minor tensor guards. This race requires a broader refactor of caching and eviction logic rather than the proposed lightweight wrapper. Please disregard the suggested diff and plan a comprehensive solution for cache consistency.Likely an incorrect or invalid review comment.
tests/unittest/llmapi/test_llm_pytorch.py (3)
336-346: Expanded LoRA eviction/reload coverage: LGTMGood scenarios (GPU-only, CPU+GPU interplay, and cache reuse after insert). The 40GB gating is appropriate for resource-heavy runs.
Also applies to: 349-359, 362-370
373-391: “Evict and reload previously evicted adapters” scenario: strong coverageThis directly validates reuse-after-CPU-eviction behavior. Thanks for encoding the expected sequence of cache operations in the docstring.
430-447: Override behavior test: LGTMConfirms that LoraConfig cache sizes override peft_cache_config; properly gated and consistent with new behavior.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
approving on behalf of nemotron devs
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM on the llmapi changes
|
/bot run |
|
PR_Github #14799 [ run ] triggered by Bot |
|
PR_Github #14799 [ run ] completed with state |
Description
Cherrypick of merge commit of #6510
Added support for reusing a LoRA adapter after it was evicted from LoRA CPU cache:
py_lora_pathoptional field toRequestand toLlmRequestclasses (only in python).PeftCacheManager.add_request_peftwould load the LoRA adapter if it's not loaded in cache.Test Coverage
tests/unittest/llmapi/test_llm_pytorch.py::test_llama_7b_multi_lora_evict_and_reload_evicted_adapters_in_cpu_and_gpu_cachetests/unittest/llmapi/test_llm_pytorch.py::test_llama_7b_multi_lora_read_from_cache_after_insertGitHub Bot Help
/bot [-h] ['run', 'kill', 'skip', 'reuse-pipeline'] ...Provide a user friendly way for developers to interact with a Jenkins server.
Run
/bot [-h|--help]to print this help message.See details below for each supported subcommand.
run [--reuse-test (optional)pipeline-id --disable-fail-fast --skip-test --stage-list "A10-PyTorch-1, xxx" --gpu-type "A30, H100_PCIe" --test-backend "pytorch, cpp" --add-multi-gpu-test --only-multi-gpu-test --disable-multi-gpu-test --post-merge --extra-stage "H100_PCIe-TensorRT-Post-Merge-1, xxx" --detailed-log --debug(experimental)]Launch build/test pipelines. All previously running jobs will be killed.
--reuse-test (optional)pipeline-id(OPTIONAL) : Allow the new pipeline to reuse build artifacts and skip successful test stages from a specified pipeline or the last pipeline if no pipeline-id is indicated. If the Git commit ID has changed, this option will be always ignored. The DEFAULT behavior of the bot is to reuse build artifacts and successful test results from the last pipeline.--disable-reuse-test(OPTIONAL) : Explicitly prevent the pipeline from reusing build artifacts and skipping successful test stages from a previous pipeline. Ensure that all builds and tests are run regardless of previous successes.--disable-fail-fast(OPTIONAL) : Disable fail fast on build/tests/infra failures.--skip-test(OPTIONAL) : Skip all test stages, but still run build stages, package stages and sanity check stages. Note: Does NOT update GitHub check status.--stage-list "A10-PyTorch-1, xxx"(OPTIONAL) : Only run the specified test stages. Examples: "A10-PyTorch-1, xxx". Note: Does NOT update GitHub check status.--gpu-type "A30, H100_PCIe"(OPTIONAL) : Only run the test stages on the specified GPU types. Examples: "A30, H100_PCIe". Note: Does NOT update GitHub check status.--test-backend "pytorch, cpp"(OPTIONAL) : Skip test stages which don't match the specified backends. Only support [pytorch, cpp, tensorrt, triton]. Examples: "pytorch, cpp" (does not run test stages with tensorrt or triton backend). Note: Does NOT update GitHub pipeline status.--only-multi-gpu-test(OPTIONAL) : Only run the multi-GPU tests. Note: Does NOT update GitHub check status.--disable-multi-gpu-test(OPTIONAL) : Disable the multi-GPU tests. Note: Does NOT update GitHub check status.--add-multi-gpu-test(OPTIONAL) : Force run the multi-GPU tests in addition to running L0 pre-merge pipeline.--post-merge(OPTIONAL) : Run the L0 post-merge pipeline instead of the ordinary L0 pre-merge pipeline.--extra-stage "H100_PCIe-TensorRT-Post-Merge-1, xxx"(OPTIONAL) : Run the ordinary L0 pre-merge pipeline and specified test stages. Examples: --extra-stage "H100_PCIe-TensorRT-Post-Merge-1, xxx".--detailed-log(OPTIONAL) : Enable flushing out all logs to the Jenkins console. This will significantly increase the log volume and may slow down the job.--debug(OPTIONAL) : Experimental feature. Enable access to the CI container for debugging purpose. Note: Specify exactly one stage in thestage-listparameter to access the appropriate container environment. Note: Does NOT update GitHub check status.For guidance on mapping tests to stage names, see
docs/source/reference/ci-overview.mdand the
scripts/test_to_stage_mapping.pyhelper.kill
killKill all running builds associated with pull request.
skip
skip --comment COMMENTSkip testing for latest commit on pull request.
--comment "Reason for skipping build/test"is required. IMPORTANT NOTE: This is dangerous since lack of user care and validation can cause top of tree to break.reuse-pipeline
reuse-pipelineReuse a previous pipeline to validate current commit. This action will also kill all currently running builds associated with the pull request. IMPORTANT NOTE: This is dangerous since lack of user care and validation can cause top of tree to break.
Summary by CodeRabbit
New Features
Refactor
Tests