-
Notifications
You must be signed in to change notification settings - Fork 4.1k
[EP ABI] Add weight pre-packing support to kernel-based plugin EPs #26754
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
Merged
Merged
Changes from 4 commits
Commits
Show all changes
31 commits
Select commit
Hold shift + click to select a range
4f3d004
[EP ABI] Add weight pre-packing support to kernel-based plugin EPs
adrianlizarraga e954021
Add comment about sharing of prepacked weights (cpu ep only)
adrianlizarraga fb2998b
Update Mul kernel to pre-pack input b
adrianlizarraga 5e64f79
Apply suggestions from code review
adrianlizarraga 9b1c6a2
Add comments regarding prepack allocator lifetime
adrianlizarraga c638a1a
Merge branch 'adrianl/plugin-ep-kernel-prepack' of github.com:microso…
adrianlizarraga 717ed4a
Added support for sharing pre-packed weights for cpu-accessible alloc…
adrianlizarraga bd8f6f0
Define what should happen if OrtKernelImpl::SetSharedPrePackedWeight(…
adrianlizarraga fc1fd16
Merge branch 'main' into adrianl/plugin-ep-kernel-prepack
adrianlizarraga 8b3f56c
Clean up some exception handling
adrianlizarraga 23503a1
Refactor example kernel classes (no inheritance)
adrianlizarraga 7f37ffb
Merge branch 'main' into adrianl/plugin-ep-kernel-prepack
adrianlizarraga 26eca56
Correct use of output param
adrianlizarraga 7af257b
Add more edge-case handling for PrePack() call
adrianlizarraga 515062e
API version checks
adrianlizarraga 347ce4f
Use correct SAL annotation for array parameters
adrianlizarraga 906187d
Clean up some includes
adrianlizarraga 1611fc3
Update onnxruntime/core/session/plugin_ep/ep_kernel_registration.cc
adrianlizarraga 30ca590
Remove OrtAllocator parameter from SharedPrePackedWeightCache_StoreWe…
adrianlizarraga a5342b9
Clarify what happens when SharedPrePackedWeightCache_StoreWeightData …
adrianlizarraga 51bc731
Merge branch 'main' into adrianl/plugin-ep-kernel-prepack
adrianlizarraga edf3f2c
Review comments
adrianlizarraga e94c0aa
C++ API
adrianlizarraga c8eb3c9
Improve doc for c++ api convenience class
adrianlizarraga 98e3d13
Add buffer_sizes as a parameter to OrtKernelImpl::SetSharedWeightData
adrianlizarraga c61ae41
Add comment to implementation of OrtKernelImpl::SetSharedPrePackedWeight
adrianlizarraga 02d75d2
Do not prescribe what the kernel impl should return for a situation t…
adrianlizarraga 0a84eda
Update include/onnxruntime/core/session/onnxruntime_ep_c_api.h
adrianlizarraga 5f80f9d
Adjust comments
adrianlizarraga c60472d
Tweak comment again
adrianlizarraga 441c9e2
Add comments to clarify ownership scenarios
adrianlizarraga File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
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
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
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
47 changes: 47 additions & 0 deletions
47
onnxruntime/test/autoep/library/example_plugin_ep_kernel_registry/ep_allocator.h
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| // Copyright (c) Microsoft Corporation. All rights reserved. | ||
| // Licensed under the MIT License. | ||
|
|
||
| #pragma once | ||
|
|
||
| #include "../plugin_ep_utils.h" | ||
|
|
||
| #include <memory> | ||
|
|
||
| // `OrtAllocator` is a C API struct. `BaseAllocator` is a minimal C++ struct which inherits from `OrtAllocator`. | ||
| // Notably, `BaseAllocator` has a virtual destructor to enable a derived class to be deleted through a `BaseAllocator` | ||
| // pointer. Allocators which need to be deleted through a base class pointer should inherit from `BaseAllocator`. | ||
| struct BaseAllocator : OrtAllocator { | ||
| virtual ~BaseAllocator() = default; | ||
| }; | ||
|
|
||
| using AllocatorUniquePtr = std::unique_ptr<BaseAllocator>; | ||
|
|
||
| struct CustomAllocator : BaseAllocator { | ||
| CustomAllocator(const OrtMemoryInfo* mem_info) : memory_info{mem_info} { | ||
| version = ORT_API_VERSION; | ||
| Alloc = AllocImpl; | ||
| Free = FreeImpl; | ||
| Info = InfoImpl; | ||
| Reserve = AllocImpl; // no special reserve logic and most likely unnecessary unless you have your own arena | ||
| GetStats = nullptr; | ||
| AllocOnStream = nullptr; | ||
| } | ||
|
|
||
| static void* ORT_API_CALL AllocImpl(struct OrtAllocator* /*this_*/, size_t size) { | ||
| return malloc(size); | ||
| } | ||
|
|
||
| /// Free a block of memory previously allocated with OrtAllocator::Alloc | ||
| static void ORT_API_CALL FreeImpl(struct OrtAllocator* /*this_*/, void* p) { | ||
| return free(p); | ||
| } | ||
|
|
||
| /// Return a pointer to an ::OrtMemoryInfo that describes this allocator | ||
| static const struct OrtMemoryInfo* ORT_API_CALL InfoImpl(const struct OrtAllocator* this_) { | ||
| const CustomAllocator& impl = *static_cast<const CustomAllocator*>(this_); | ||
| return impl.memory_info; | ||
| } | ||
|
|
||
| private: | ||
| const OrtMemoryInfo* memory_info; | ||
| }; |
115 changes: 115 additions & 0 deletions
115
onnxruntime/test/autoep/library/example_plugin_ep_kernel_registry/ep_data_transfer.cc
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,115 @@ | ||
| // Copyright (c) Microsoft Corporation. All rights reserved. | ||
| // Licensed under the MIT License. | ||
|
|
||
| #include "ep_data_transfer.h" | ||
|
|
||
| #include <cassert> | ||
| #include <gsl/span> | ||
|
|
||
| /*static*/ | ||
| bool ORT_API_CALL ExampleDataTransfer::CanCopyImpl(const OrtDataTransferImpl* this_ptr, | ||
| const OrtMemoryDevice* src_memory_device, | ||
| const OrtMemoryDevice* dst_memory_device) noexcept { | ||
| const auto& impl = *static_cast<const ExampleDataTransfer*>(this_ptr); | ||
| bool src_is_our_device = impl.ep_api_.MemoryDevice_AreEqual(src_memory_device, impl.device_mem_info); | ||
| bool dst_is_our_device = impl.ep_api_.MemoryDevice_AreEqual(dst_memory_device, impl.device_mem_info); | ||
|
|
||
| if (src_is_our_device && dst_is_our_device) { | ||
| return true; | ||
| } | ||
|
|
||
| // implementation should check if the copy is possible, which may require checking the device type, the memory type | ||
| // and the vendor and device IDs as needed. | ||
| OrtMemoryInfoDeviceType src_device_type = impl.ep_api_.MemoryDevice_GetDeviceType(src_memory_device); | ||
| OrtMemoryInfoDeviceType dst_device_type = impl.ep_api_.MemoryDevice_GetDeviceType(dst_memory_device); | ||
| OrtDeviceMemoryType src_mem_type = impl.ep_api_.MemoryDevice_GetMemoryType(src_memory_device); | ||
| OrtDeviceMemoryType dst_mem_type = impl.ep_api_.MemoryDevice_GetMemoryType(dst_memory_device); | ||
|
|
||
| // we can copy to/from CPU or CPU accessible memory | ||
| if (src_is_our_device) { | ||
| return (dst_device_type == OrtMemoryInfoDeviceType_CPU || dst_mem_type == OrtDeviceMemoryType_HOST_ACCESSIBLE); | ||
| } | ||
|
|
||
| if (dst_is_our_device) { | ||
| return (src_device_type == OrtMemoryInfoDeviceType_CPU || src_mem_type == OrtDeviceMemoryType_HOST_ACCESSIBLE); | ||
| } | ||
|
|
||
| return false; | ||
| } | ||
|
|
||
| namespace { | ||
| void CopyImpl(const void* src_data, void* dst_data, size_t bytes, OrtSyncStream* stream) { | ||
| // in our example setup this is really CPU to CPU | ||
|
|
||
| if (stream) { | ||
| // EP can do an async copy using the stream. e.g. an NVIDIA EP would provide the stream to cudaMemcpyAsync | ||
| } | ||
|
|
||
| if (src_data != dst_data) { | ||
| memcpy(dst_data, src_data, bytes); | ||
| } | ||
| } | ||
| } // namespace | ||
|
|
||
| // function to copy one or more tensors. | ||
| // implementation can optionally use async copy if a stream is available for the input. | ||
| /*static*/ | ||
| OrtStatus* ORT_API_CALL ExampleDataTransfer::CopyTensorsImpl(OrtDataTransferImpl* this_ptr, | ||
| const OrtValue** src_tensors_ptr, | ||
| OrtValue** dst_tensors_ptr, | ||
| OrtSyncStream** streams_ptr, | ||
| size_t num_tensors) noexcept { | ||
| auto& impl = *static_cast<ExampleDataTransfer*>(this_ptr); | ||
|
|
||
| auto src_tensors = gsl::make_span<const OrtValue*>(src_tensors_ptr, num_tensors); | ||
| auto dst_tensors = gsl::make_span<OrtValue*>(dst_tensors_ptr, num_tensors); | ||
|
|
||
| for (size_t i = 0; i < num_tensors; ++i) { | ||
| // the implementation for a 'real' EP would be something along these lines. | ||
| // See CudaDataTransferImpl in onnxruntime\core\providers\cuda\cuda_provider_factory.cc | ||
| const OrtMemoryDevice* src_device = impl.ep_api_.Value_GetMemoryDevice(src_tensors[i]); | ||
| const OrtMemoryDevice* dst_device = impl.ep_api_.Value_GetMemoryDevice(dst_tensors[i]); | ||
|
|
||
| OrtMemoryInfoDeviceType src_device_type = impl.ep_api_.MemoryDevice_GetDeviceType(src_device); | ||
| OrtMemoryInfoDeviceType dst_device_type = impl.ep_api_.MemoryDevice_GetDeviceType(dst_device); | ||
|
|
||
| // OrtDeviceMemoryType src_mem_type = impl.ep_api.MemoryDevice_GetMemoryType(src_device); | ||
| // OrtDeviceMemoryType dst_mem_type = impl.ep_api.MemoryDevice_GetMemoryType(dst_device); | ||
| // bool copy_involves_host_accessible_memory = src_mem_type == OrtDeviceMemoryType_HOST_ACCESSIBLE || | ||
| // dst_mem_type == OrtDeviceMemoryType_HOST_ACCESSIBLE; | ||
|
|
||
| const void* src_data = nullptr; | ||
| void* dst_data = nullptr; | ||
| size_t bytes; | ||
|
|
||
| RETURN_IF_ERROR(impl.ort_api_.GetTensorData(src_tensors[i], &src_data)); | ||
| RETURN_IF_ERROR(impl.ort_api_.GetTensorMutableData(dst_tensors[i], &dst_data)); | ||
| RETURN_IF_ERROR(impl.ort_api_.GetTensorSizeInBytes(src_tensors[i], &bytes)); | ||
|
|
||
| if (dst_device_type == OrtMemoryInfoDeviceType_GPU) { | ||
| if (src_device_type == OrtMemoryInfoDeviceType_GPU) { | ||
| // GPU -> GPU | ||
| } else { | ||
| // CPU -> GPU | ||
| } | ||
| } else if (src_device_type == OrtMemoryInfoDeviceType_GPU) { | ||
| // GPU -> CPU | ||
| } else { | ||
| // CPU -> CPU. may involve copy a to/from host accessible memory and a synchronize may be required first | ||
| } | ||
|
|
||
| // but in our example EP it's simpler as it's really a (fake) CPU to CPU copy | ||
| CopyImpl(src_data, dst_data, bytes, streams_ptr ? streams_ptr[i] : nullptr); | ||
| } | ||
|
|
||
| return nullptr; | ||
| } | ||
|
|
||
| /*static*/ | ||
| void ORT_API_CALL ExampleDataTransfer::ReleaseImpl(OrtDataTransferImpl* /*this_ptr*/) noexcept { | ||
| // In our setup the factory owns a shared ExampleDataTransfer instance so it will do the cleanup, and we ignore | ||
| // the call to Release from the plugin_ep::DataTransfer dtor (see /onnxruntime/core/framework/plugin_data_transfer.h) | ||
| // | ||
| // If you create a new instance on each call to OrtEpFactory::CreateDataTransfer you call `delete` here | ||
| // delete static_cast<ExampleDataTransfer*>(this_ptr); | ||
| } |
34 changes: 34 additions & 0 deletions
34
onnxruntime/test/autoep/library/example_plugin_ep_kernel_registry/ep_data_transfer.h
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| // Copyright (c) Microsoft Corporation. All rights reserved. | ||
| // Licensed under the MIT License. | ||
|
|
||
| #pragma once | ||
|
|
||
| #include "../plugin_ep_utils.h" | ||
|
|
||
| struct ExampleDataTransfer : OrtDataTransferImpl { | ||
| ExampleDataTransfer(const OrtApi& ort_api, const OrtEpApi& ep_api, | ||
| const OrtMemoryDevice* device_mem_info_) | ||
| : ort_api_(ort_api), ep_api_(ep_api), device_mem_info{device_mem_info_} { | ||
| CanCopy = CanCopyImpl; | ||
| CopyTensors = CopyTensorsImpl; | ||
| Release = ReleaseImpl; | ||
| } | ||
|
|
||
| static bool ORT_API_CALL CanCopyImpl(const OrtDataTransferImpl* this_ptr, | ||
| const OrtMemoryDevice* src_memory_device, | ||
| const OrtMemoryDevice* dst_memory_device) noexcept; | ||
|
|
||
| // function to copy one or more tensors. | ||
| // implementation can optionally use async copy if a stream is available for the input. | ||
| static OrtStatus* ORT_API_CALL CopyTensorsImpl(OrtDataTransferImpl* this_ptr, | ||
| const OrtValue** src_tensors_ptr, | ||
| OrtValue** dst_tensors_ptr, | ||
| OrtSyncStream** streams_ptr, | ||
| size_t num_tensors) noexcept; | ||
| static void ORT_API_CALL ReleaseImpl(OrtDataTransferImpl* this_ptr) noexcept; | ||
|
|
||
| private: | ||
| const OrtApi& ort_api_; | ||
| const OrtEpApi& ep_api_; | ||
| const OrtMemoryDevice* device_mem_info; // device our EP runs on | ||
| }; |
Oops, something went wrong.
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.