-
Notifications
You must be signed in to change notification settings - Fork 3.8k
[Hexagon] [runtime] Manage RPC and runtime buffers separately #13028
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
Changes from all commits
b602893
a50f3e5
4b99329
e9241aa
cc950f6
adc5460
dee71d3
9d3fddf
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -90,18 +90,23 @@ void* HexagonDeviceAPI::AllocDataSpace(Device dev, int ndim, const int64_t* shap | |
|
|
||
| const size_t typesize = (dtype.bits / 8) * dtype.lanes; | ||
|
|
||
| CHECK(runtime_hexbuffs) << "Attempted to allocate Hexagon data with " | ||
| << "HexagonDeviceAPI::AllocDataSpace before initializing resources. " | ||
| << "Please call HexagonDeviceAPI::AcquireResources"; | ||
|
|
||
| if (ndim == 0) { | ||
| // Allocate storage for a single scalar value. | ||
| return mgr->AllocateHexagonBuffer(typesize, kHexagonAllocAlignment, mem_scope); | ||
| return runtime_hexbuffs->AllocateHexagonBuffer(typesize, kHexagonAllocAlignment, mem_scope); | ||
| } else if (ndim == 1) { | ||
| // Allocate a single, contiguous memory region. | ||
| size_t nbytes = shape[0] * typesize; | ||
| return mgr->AllocateHexagonBuffer(nbytes, kHexagonAllocAlignment, mem_scope); | ||
| return runtime_hexbuffs->AllocateHexagonBuffer(nbytes, kHexagonAllocAlignment, mem_scope); | ||
| } else if (ndim == 2) { | ||
| // Allocate the region(s) needed for Hexagon's indirect-tensor format. | ||
| size_t nallocs = shape[0]; | ||
| size_t nbytes = shape[1] * typesize; | ||
| return mgr->AllocateHexagonBuffer(nallocs, nbytes, kHexagonAllocAlignment, mem_scope); | ||
| return runtime_hexbuffs->AllocateHexagonBuffer(nallocs, nbytes, kHexagonAllocAlignment, | ||
| mem_scope); | ||
| } else { | ||
| return nullptr; // unreachable | ||
| } | ||
|
|
@@ -115,13 +120,34 @@ void* HexagonDeviceAPI::AllocDataSpace(Device dev, size_t nbytes, size_t alignme | |
| if (alignment < kHexagonAllocAlignment) { | ||
| alignment = kHexagonAllocAlignment; | ||
| } | ||
| return mgr->AllocateHexagonBuffer(nbytes, alignment, String("global")); | ||
| CHECK(runtime_hexbuffs) << "Attempted to allocate Hexagon data with " | ||
| << "HexagonDeviceAPI::AllocDataSpace before initializing resources. " | ||
| << "Please call HexagonDeviceAPI::AcquireResources"; | ||
| return runtime_hexbuffs->AllocateHexagonBuffer(nbytes, alignment, String("global")); | ||
| } | ||
|
|
||
| void HexagonDeviceAPI::FreeDataSpace(Device dev, void* ptr) { | ||
| CHECK(ptr) << "buffer pointer is null"; | ||
| CHECK(IsValidDevice(dev)) << "dev.device_type: " << dev.device_type; | ||
| mgr->FreeHexagonBuffer(ptr); | ||
| if (runtime_hexbuffs) { | ||
| runtime_hexbuffs->FreeHexagonBuffer(ptr); | ||
| } else { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure on the value-add for this With some comments to explain why the "free" may happen after the "release".
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My previous reply is related. If there's a manager, I can just use it to call Free. If there isn't (it has been released, or maybe acquire was never called), I can check what I had allocated before Release. If that buffer is in there, I will not throw. But if there's a bug and the user is attempting to free some pointer we never knew about, I do want that to throw.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Making sure all "temporal" frees occur before Release is a good idea and worthwhile endeavor. ✅ I am not convinced that two |
||
| auto it = std::find(released_runtime_buffers.begin(), released_runtime_buffers.end(), ptr); | ||
| CHECK(it != released_runtime_buffers.end()) << "Attempted to free Hexagon data with " | ||
| << "HexagonDeviceAPI::FreeDataSpace that was not " | ||
| << "allocated during the session."; | ||
| } | ||
| } | ||
|
|
||
| void* HexagonDeviceAPI::AllocRpcBuffer(size_t nbytes, size_t alignment) { | ||
| CHECK(nbytes) << "number of bytes is zero"; | ||
| CHECK(alignment) << "alignment is zero"; | ||
janetsc marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| return rpc_hexbuffs.AllocateHexagonBuffer(nbytes, alignment, String("global")); | ||
| } | ||
|
|
||
| void HexagonDeviceAPI::FreeRpcBuffer(void* ptr) { | ||
| CHECK(ptr) << "buffer pointer is null"; | ||
| rpc_hexbuffs.FreeHexagonBuffer(ptr); | ||
| } | ||
|
|
||
| // WorkSpace: runtime allocations for Hexagon | ||
|
|
@@ -137,7 +163,10 @@ void* HexagonDeviceAPI::AllocWorkspace(Device dev, size_t size, DLDataType type_ | |
|
|
||
| void HexagonDeviceAPI::FreeWorkspace(Device dev, void* data) { | ||
| CHECK(IsValidDevice(dev)) << "dev.device_type: " << dev.device_type; | ||
| CHECK(mgr->count(data) != 0) | ||
| CHECK(runtime_hexbuffs) << "Attempted to free Hexagon workspace with " | ||
| << "HexagonDeviceAPI::FreeWorkspace outside of a session. " | ||
| << "Please call HexagonDeviceAPI::AcquireResources"; | ||
| CHECK(runtime_hexbuffs->count(data) != 0) | ||
| << "Attempt made to free unknown or already freed workspace allocation"; | ||
| dmlc::ThreadLocalStore<HexagonWorkspacePool>::Get()->FreeWorkspace(dev, data); | ||
| } | ||
|
|
@@ -160,8 +189,13 @@ void HexagonDeviceAPI::CopyDataFromTo(DLTensor* from, DLTensor* to, TVMStreamHan | |
| CHECK_EQ(from->byte_offset, 0); | ||
| CHECK_EQ(to->byte_offset, 0); | ||
| CHECK_EQ(GetDataSize(*from), GetDataSize(*to)); | ||
| CHECK(runtime_hexbuffs) << "Attempted to copy Hexagon data with " | ||
| << "HexagonDeviceAPI::CopyDataFromTo before initializing resources. " | ||
| << "Please call HexagonDeviceAPI::AcquireResources"; | ||
|
|
||
| auto lookup_hexagon_buffer = [this](void* ptr) -> HexagonBuffer* { return mgr->find(ptr); }; | ||
| auto lookup_hexagon_buffer = [this](void* ptr) -> HexagonBuffer* { | ||
| return runtime_hexbuffs->find(ptr); | ||
| }; | ||
|
|
||
| HexagonBuffer* hex_from_buf = lookup_hexagon_buffer(from->data); | ||
| HexagonBuffer* hex_to_buf = lookup_hexagon_buffer(to->data); | ||
|
|
||
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.
I apologize for the post-merge feedback. But ... having this method seems like a break in the abstraction of the
HexagonBufferManagerclass, to me. If we have to do this it feels like something else is amiss.Uh oh!
There was an error while loading. Please reload this page.
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.
Thanks for the review.
I agree, this isn't ideal. However, I wanted to make sure that I don't give a "free pass" to all frees that come after ReleaseResources. The scenario I was trying to avoid was:
User allocates buffer B
Session ends, ReleaseResources is called and B is freed
User frees buffer B as objects are being torn down, which should not throw as it is somewhat expected
User frees buffer C, which SHOULD throw, as it was never allocated in the first place
I added this so that I can check if there's no runtime_hexbuffs, then I make sure that what is being attempted to be freed is something that we owned before ReleaseResources was called. In which case, that Free is a no-op. I also want to detect spurious calls to Free that wouldn't have been legit before ReleaseResources was called.
I'm happy to discuss more, and brainstorm other ways to accomplish this.
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.
As an alternative, what if the
HexagonBufferManagerallows three states for buffers instead of two.That way, instead of discarding the
HexagonBufferManager runtime_hexbuffsaltogether when the session closes, it instead moves all keys from the internalstd::unordered_map<void*, std::unique_ptr<HexagonBuffer>> hexagon_buffer_map_;to an internalstd::unordered_set<void*>. This avoids exposing any of the buffers to the outside, but does allow theHexagonBufferManagerto identify a previously freed pointer.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.
Thanks @Lunderberg, I think your suggestion would satisfy my concern. What do you think @janetsc? This would allow us to make valid frees a
nopand to catch invalid frees as well. If I am understanding correctlyruntime_hexbuffswould be instantiated by the Hexagon Device API in the same way that we instantiate therpc_hexbuffs...HexagonBufferManager runtime_hexbuffs;? If that is the case, we could potentially have just one HeagonBufferManager that implements the API and internal state that @Lunderberg suggests.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.
There's also another state not captured - the buffer manager is in an invalid state to allocate more memory.
There needs to be two buffer managers, because on ReleaseResources how do we know which buffers need to be freed?
I'm thinking that a better model is to enforce that all frees should take place before ReleaseResources. That's cleaner. I will investigate which ones are outstanding. All objects should be cleanly torn down by the time the session ends. I will file a task to investigate.