G-API: fix OCL backend issue with UMat lifetime#14985
Conversation
Add tests on initialized/uninitialized outputs for all backends
|
@dmatveev @dbudniko @rgarnov @anton-potapov please review |
|
Tagged "WIP": need to investigate whether clean up procedure is valid |
| cv::Size(250, 320), // out size < in size | ||
| cv::Size(1920, 1080), // out size > in size | ||
| cv::Size(640, 400), // out width = in width | ||
| cv::Size(10, 480) // out height = in height |
There was a problem hiding this comment.
Testing the above contracts by passing a cartesian product of combinations is not the right way to go. Every test should be expessed separately to clearly show what it means.
|
@dmatveev let's make a proper test for output in a different PR, from your feedback it looks like quite a lot of code that can be better reviewed separately. I reduced this test to only reproduce the issue and it takes ~300 ms for this "test suite" to complete (all backends), so I guess we're safe to add it "as is" as an additional coverage |
|
I see some bug fixed here but can't find any description on that. Please add it here. |
|
@dmatveev added the issue to the description |
|
@andrey-golubev thanks! the description is now really nice & detailed.
So according to this text, we do not pass the existing dangling UMat to OCL for execution but instead create a new one for the (changed) output buffer? So why it crashes then? |
|
@dmatveev I think step (3) is misleading: will try to re-phrase. We have this dangling pointer implicitly already in OCL (at step (2)) because we set it to OCL runtime with Now on the second call, before creating new UMat, we release the previous one: as we release it with |
|
Maybe @alalek can add up to my explanation |
Thanks, now it's clear. So if UMat/Mat relationship is designed this way, it seems the only proper thing to do is to drop our input/output UMat objects after execution? |
dmatveev
left a comment
There was a problem hiding this comment.
Did u measure the performance on master against this? How bad is this now? Can u share a diff please? (there's an OpenCV script for that)
| // For example, if the original output (cv::Mat) is re-initialized by the user but we still | ||
| // hold cv::UMat -> we get cv::UMat that has a parent that was already destroyed. Also, | ||
| // since we don't own the data (the user does), there's no point holding it after we're done | ||
| const auto clean_up = [&input_objs, &output_objs] (cv::gimpl::Mag* p) |
There was a problem hiding this comment.
due to std::unique_ptr usage below: pointer to object is expected as an input/output parameter
| for (auto& it : output_objs) umats.erase(it.first.id); | ||
| }; | ||
| // RAII wrapper to clean-up m_res | ||
| std::unique_ptr<cv::gimpl::Mag, decltype(clean_up)> cleaner(&m_res, clean_up); |
There was a problem hiding this comment.
I am not sure I understand what you mean here. Figured out that std::unique_ptr with custom deleter is less boilerplate than custom Mag wrapper class
|
@dmatveev answered you below:
Since we create UMats for input/output internally and in place, yes, I think right now it's the only proper option (the other would be to allow user to pass UMats as input/output but it's a different story). As for the perf. measurements: didn't check yet, will do and share |
|
Marked as "WIP": please do not merge yet. I have one more rabbit hole to go down |
|
Didn't we agree to day to merge this stuff? Thanks! 👍 |
|
@dmatveev indeed we agreed. Will add a task to our tracking system. Going to check that everything works with latest master and we can merge after that |
|
@alalek please feel free to merge |
|
Thanks! |
1 similar comment
|
Thanks! |
* G-API: fix GOCLExecutable issue with UMat lifetime Add tests on initialized/uninitialized outputs for all backends * Use proper clean-up procedure for magazine * Rename InitOut test and reduce tested sizes * Enable output allocation test
* G-API: fix GOCLExecutable issue with UMat lifetime Add tests on initialized/uninitialized outputs for all backends * Use proper clean-up procedure for magazine * Rename InitOut test and reduce tested sizes * Enable output allocation test
This pullrequest
The issue can be described in a code snippet
let's investigate:
step (1) successfully executes the graph and result is written into
output_mat.Internally, (since we use OpenCL backend) output data in form of UMat is still held by the backend. Note: we construct UMat inside the backend
step (2) we reset the output buffer, now remember that at step (1) there's a lonely UMat somewhere inside the G-API backend. Now what we have in this lonely UMat is a dangling pointer to the original data (
UMatData::origdata) that still points to already re-allocatedMat::data. What's more: this original data pointer is also "captured" by OCL runtime since we explicitly set thisUMatData::origdataashost_ptrtoclCreateBuffer()at step (3) comes the point of no return: we need to store newly arrived output data into internal container inside OpenCL backend to execute our graph and write result to that buffer. We do it via assignment operation and previously created UMat (that lonely guy from step (2)) is queued for destruction: but its
host_ptris dangling so when we doclReleaseMemObject()the UB starts. When we request new UMat from new output buffer (assuming this happens after "invalid"clReleaseMemObject()) there's a segmentation fault inclCreateBuffer()- this is the next call to OCL runtime in my case so it failsBottom line: we get segmentation fault inside OpenCL due to the following:
I am not sure exactly what happens inside the OpenCL but I see that
void* data = clEnqueueMapBuffer(q, (cl_mem)u->handle, CL_TRUE, (CL_MAP_READ | CL_MAP_WRITE), 0, u->size, 0, 0, 0, &retval);returns the same (already dangling) pointer (there's explicitCV_Assert(u->origdata == data)).So what we get after step (2) is the dangling pointer inside OpenCL and we explicitly ask OpenCL to clear the data by that pointer using the cl_mem handle => undefined behavior
I have the following configuration with which I am able to reproduce the issue:
(using Ubuntu 16.04.1 kernel 4.15.0-54-generic x86_64 GNU/Linux)