CARVIEW |
Navigation Menu
-
-
Notifications
You must be signed in to change notification settings - Fork 56.2k
G-API: oneVPL - Performance: Add async decode pipeline & add cached pool #20901
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
Conversation
modules/gapi/src/streaming/onevpl/accelerators/surface/surface_pool.cpp
Outdated
Show resolved
Hide resolved
modules/gapi/src/streaming/onevpl/engine/decode/decode_engine_legacy.cpp
Show resolved
Hide resolved
GAPI_LOG_WARNING(nullptr, "[" << my_sess.session << | ||
"] has no surface, reason: " << | ||
ex.what()); | ||
// TODO it is supposed to place `break;` here |
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.
Is it Ok to get into this catch
? I mean even now it can be just exited correctly?
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.
The same comment can be applied to all the catch
blocks below
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.
it's OK here: swap_surface
can throw exception in case of no available "free" surface is present in pool. This lack-of-resource state processing (like std::bad_alloc
for new
allocation ) allows us to go on async-waiting result state and put off decoder queuing job until one or more async result completed and resource freeing
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.
The issue of bad_alloc
is that it can be handled explicitly - here we handle all the exceptions uniformly and this bad-alloc / general error difference is not clear to me
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.
This is the reason why I adhered my point about using abort()
in GAPI_Assert
instead of throwing exception:
because abort()
means the library itself problem which is not supposed to be handed by user but introducing BUG to fix. Otherwise exception
has recoverable semantic
In current catch block we could face with two different kind of exception:
swap_surface ->get_free_surface() -> find KEY in pool (exception means library problem and supposed to be abort() with BUG/Issue creation)
and the second
swap_surface ->get_free_surface()-> pool.find_free() -> no free surface at the time (resource unavailable at moment)
The second is expected to be processed.
No other exception types is throwing.
So, according to idea that GAPI_Assert has no abort
inside - we need to catch only second case. So it make sense to find out what types of exception is throwing by GAPI_Assert and sort out different types in pool.find_free
modules/gapi/src/streaming/onevpl/engine/decode/decode_engine_legacy.cpp
Show resolved
Hide resolved
using namespace cv::gapi::wip::onevpl; | ||
|
||
const auto params = GetParam(); | ||
source_t src = findDataFile(get<0>(params)); |
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.
should we put false
here as second argument to not complain on file not found error? Or I am wrong?
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.
maybe yes, i will add it
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.
looks like false
is confusing here. it added but has reverted
From my perspective:
- if user didn't configured EXTRA path in here initTestDataPathSilent() then
SkipException
is OK here - IF user did configured EXTRA path here, then IT MUST contain requested test files. So
findDataFile
must request it availability
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.
Please align it with the latest news from @mpashchenkov
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.
asked a detailed question in @mpashchenkov PR by #20995 (comment)
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.
#20995 (comment)
So:
test must fail if no env OPENCV_TEST_DATA_PATH is set (in case of test requires external test data)
test must fail if OPENCV_TEST_DATA_PATH set and filepath is incorrect.
Looks like i need to remove my initTestDataPathSilent
check
|
||
const auto params = GetParam(); | ||
source_t src = findDataFile(get<0>(params)); | ||
codec_t type = get<1>(params); |
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.
Don't you find using std::tie(src, type) = params;
better?
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 for me this transforms into the same 3 lines
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.
The idea behind using std::tie
instead of get<N>
is type safety
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'm not sure that it is important in UTs, because an one tuple type is for a single one test and when you changed the tuple set - it had caused by the reason that you intended to change test by itself.
If a some common
tuple set (hypothetically) was changed by adding new type for a some specific single new one test then it is more convenient for me to do not brake ALL tests which depended from common
tuple set by adding std::ignore
for all of them (in case if i have 100500 legacy unit tests).
Additionally, AND which is more important for me: please compare
std::string path;
...
std::tie(path,...) = GetParams();
path = findDataFile(path);
...
vs
auto path = findDataFile(std::get<0>(param));
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.
because an one tuple type is for a single one test and when you changed the tuple set - it had caused by the reason that you intended to change test by itself.
Not always, unfortunately - sometimes these parameters are introduced only for particular tests in the suite and the rest is not updated.
std::vector<CfgParam> cfg_params { | ||
CfgParam::create<std::string>("mfxImplDescription.Impl", "MFX_IMPL_TYPE_HARDWARE"), | ||
CfgParam::create("mfxImplDescription.mfxDecoderDescription.decoder.CodecID", type), | ||
}; |
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.
Why there are difference between instantiations? std::string
is not deducible?
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.
first function deduces template argument as const char*
which uses as type for construction cv::variant
later.
cv::variant
is pretty different from std::variant
and cannot support construct_as
semantic for std::string
(At the moment when current PR introduced). Thus explicit template instantiation requested here due to limited functionality of cv::variant
std::count_if(surfaces.begin(), surfaces.end(), | ||
[](const surface_ptr_t& val) { | ||
GAPI_DbgAssert(val && "Pool contains empty surface"); | ||
return !val->get_locks_count(); |
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.
it is still more tricky to read !int()
Is this some common way to represent logic statement in some famous libraries (likes STD)?
Just a minor question, not to address!
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 wonder when you asked - as for me it's well known expression, especially in C in the same way as if (num %2)
and so on
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.
+1 to Asya's point, get_lock_count() == 0
is more straight to understand
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.
ok will change by majority request
// prepare sync object for new surface | ||
LegacyDecodeSession::op_handle_t sync_pair{}; | ||
|
||
// enqueue qecode operation with current session surface |
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.
sorry for such comment!
qecode
-> decode
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
ready_frames.emplace(cv::MediaFrame(std::move(frame_adapter)), sess.generate_frame_meta()); | ||
|
||
// pop away ready sync onject |
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.
object
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
|
||
if (my_sess.last_status == MFX_ERR_NONE) { | ||
my_sess.sync_queue.emplace(sync_pair); | ||
} else if (MFX_ERR_MORE_DATA != my_sess.last_status) /* suppress MFX_ERR_MORE_DATA warning */ { |
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.
different order of operands in comparison against if
above, however, such order is mostly required for ==
comparison...
is it intended?
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.
it was written in different times of day :)
using namespace cv::gapi::wip::onevpl; | ||
|
||
const auto params = GetParam(); | ||
source_t src = findDataFile(get<0>(params)); |
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.
we usually use std::tie
instead of std::get<>
}; | ||
|
||
cv::gapi::wip::IStreamSource::Ptr source_ptr; | ||
try { |
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.
hmmm, wondering why it isn't under ifdef
?
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.
it makes sense.
What is the current order for UT: to skip or to disable ?
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.
added #ifdef HAVE_ONEVPL
in the beginning
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.
disable (put under ifdef
)
// create empty pool | ||
pool_t pool; | ||
pool.reserve(pool_size); | ||
// create empty pool with reservation |
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'd start with capital and usually we use these tags before comment:
NB
, FIXME
, TODO
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.
sorry, what is NB
?
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.
Nota bene
|
||
const auto params = GetParam(); | ||
source_t src = findDataFile(get<0>(params)); | ||
codec_t type = get<1>(params); |
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.
The idea behind using std::tie
instead of get<N>
is type safety
modules/gapi/perf/streaming/gapi_streaming_source_perf_tests.cpp
Outdated
Show resolved
Hide resolved
TEST_CYCLE() | ||
{ | ||
source_ptr->pull(out); | ||
} |
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.
to be honest I'm not sure how well it maps to the idea of a performance test this way.
TEST_CYCLE()
measures the same thing again and again to calculate its statistic values. Here you actually decode different frames, but measure it as the same random value - not sure if it is correct.
Maybe running the whole file under the same TEST_CYCLE
would make sense w.r.t stats, but then this test can take foerever...
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's true, because different frames take up different decode duration but...
It's supposed to be compared in performance metrics against VideoCap
on the same input streams (and frames) AND find out hotspots by using Vtune metrics in overall pipeline. I means auxiliary code around on direct MFX decode api
Wrapping TEST_CYCLE for all case and to incorporate a source creation also will measure MFX library/DX11 CoCreate load/unload and so on and doesn't give me relative result performance in comparison with VideoCapdecode api
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.
Wrapping TEST_CYCLE for all case and to incorporate a source creation also will measure MFX library/DX11 CoCreate load/unload and so on and doesn't give me relative result performance in comparison with VideoCapdecode api
This makes sense, but can it be done only once before the TEST_CYCLE()
body kicks in? Is there a way to decompose it to prologue/epilogue so that initialization/deinitialization for every individual run is left out of profile?
|
||
using namespace cv::gapi::wip; | ||
|
||
source_t src = findDataFile(GetParam(), false); |
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 believe this is the method which throws SkipTestException
so we don't need to test it on our own
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.
It receives initial PATH and return modified PATH according to the OPENCV_TEST_DAT_PATH prefix but for only single case if file is found by path in result.
So, if path doesn't find then no modified PATH returned and no exception was throws in case of second false
params.
So i was confused by changing second params to false
Let's remove false
here - false
means optional
, so when requested file doesn't found it returns non-modified path and throw Skip otherwise.
My suggestions:
if initTestDataPathSilent
is success, that means OPENCV EXTRA is present that MUST provide test video file by requested path otherwise configuration is incorrect. so false
must be removed
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.
Please align with @mpashchenkov
// create empty pool | ||
pool_t pool; | ||
pool.reserve(pool_size); | ||
// create empty pool with reservation |
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.
Nota bene
GAPI_LOG_WARNING(nullptr, "[" << my_sess.session << | ||
"] has no surface, reason: " << | ||
ex.what()); | ||
// TODO it is supposed to place `break;` here |
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.
The issue of bad_alloc
is that it can be handled explicitly - here we handle all the exceptions uniformly and this bad-alloc / general error difference is not clear to me
sess.swap_surface(*this); | ||
return ExecutionStatus::Continue; | ||
} catch (const std::exception& ex) { |
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.
here too
try { | ||
sess.swap_surface(*this); | ||
return ExecutionStatus::Continue; | ||
} catch (const std::exception& ex) { |
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.
and here too
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 :+1
}; | ||
|
||
cv::gapi::wip::IStreamSource::Ptr source_ptr; | ||
try { |
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.
disable (put under ifdef
)
1220067
to
1d8df9f
Compare
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.
Ok to merge
@alalek Could you merge it please? alignment with VPL builder would be done later when all parts of vpl source merged |
…perf_mod G-API: oneVPL - Performance: Add async decode pipeline & add cached pool * Add async decode pipeline & intro cached pool * Fix performacne test with checking OPENCV_EXTRA * Add sip perf test with no VPL * Fix misprint * Remove empty line.. * Apply some comments * Apply some comments * Make perf test fail if no OPENCV_TEST_DATA_PATH declared
This PR is a part of large PR #20469
Improve performance of VPL pipeline processing:
Pull Request Readiness Checklist
See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request
Patch to opencv_extra has the same branch name.
Build configuration