-
Notifications
You must be signed in to change notification settings - Fork 2.3k
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
[NPU] Blob versioning #27159
base: master
Are you sure you want to change the base?
[NPU] Blob versioning #27159
Conversation
aaba4a6
to
421d071
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.
Small renaming suggestion for createMetadata()
.
a49bbba
to
f1a9f1c
Compare
build_jenkins |
cb04bf6
to
4832272
Compare
|
||
virtual void set_version(uint32_t newVersion) = 0; | ||
|
||
virtual void set_ov_version(OpenvinoVersion& newVersion) = 0; |
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.
Do we need this in the base class? I only see it used in a test, where we don't need a reference to the base class.
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.
Done.
|
||
void OpenvinoVersion::read(std::istream& stream) { | ||
stream.read(reinterpret_cast<char*>(&size), sizeof(size)); | ||
stream.read(&version[0], size); |
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.
No resize before 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.
I tried doing it in the past but it impacted the run-time by about 200ms, so I took it out. But, having it tried again now, I don't notice any slowness.
Then I will add it back.
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.
Done.
/** | ||
* @brief Returns a uint32_t value which represents two uint16_t values concatenated. | ||
* | ||
* @param major Major version. |
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.
Kudos for attempting to document everything, but I find little utility in attaching obvious descriptions to parameters (still, it's better than no documentation at all). For instance, maybe here you could elaborate what the major/minor version represent or even the difference between them (the one established by convention).
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.
Done.
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, but not really what I was looking for. First, you may place more verbose descriptions alongside "param" fields as well, otherwise their usefulness is quite low. Second, my comment served as an encouragement to do the same for all function definitions, not only this one.
This stuff holds little-to-no importance anyway, so feel free to disregard it.
} | ||
|
||
void OpenvinoVersion::read(std::istream& stream) { | ||
stream.read(reinterpret_cast<char*>(&size), sizeof(size)); |
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.
stream.read(reinterpret_cast<char*>(&size), sizeof(size)); | |
stream >> size; |
Doesn't this work the same way, in a more C++ way? Please double check if you agree, I'm not 100% sure.
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.
Sadly, it does not. The >>
operator writes any value as a string. This is why we need to use the &
operator to access the data from the memory in binary representation. This also covers the problem of the size being read for each field inside the blob.
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 me it sounds like the issue is you've copied the data from the original stream (a file stream) into your own stringstream
in a binary manner. Avoiding to create your own intermediate stream as suggested in other comments should remove this constraint.
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.
@lmielick Just asking for one more opinion. Do you see any issue if we read/write some multi-byte variables in a raw binary manner? Regarding cross-platform compatibility, here we're using data types of fixed byte size, and I'm not aware of any requirement asking us to support big-endian platforms. Also wondering if the big-endian - little-endian compatibility isn't already broken due to the ELF part of the blob.
We're trying to decide whethere to use the read/write or <<
/>>
stream operators in order to attach plugin metdata to the blob. The latter would cost a little bit more storage due to its string-like format, the cost may increase if we decide to expand this feature.
return storedMeta; | ||
} | ||
|
||
void Metadata<METADATA_VERSION_1_0>::set_version(uint32_t newVersion) { |
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.
Any reason to offer a setter for 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.
For my unit tests, since the field is private.
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.
Not a fan of modeling the source code in order to make tests implementation more convenient. Instead, will it work if you define Metadata<DUMMY_VERSION>
in the tests file, and have the ctor place an invalid version there?
version = newVersion; | ||
} | ||
|
||
void Metadata<METADATA_VERSION_1_0>::set_ov_version(OpenvinoVersion& newVersion) { |
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.
Same as above.
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.
Same answer as above.
* @param minor Minor version. | ||
* @return Major and minor versions concatenated into one single uint32_t value. | ||
*/ | ||
constexpr uint32_t make_version(uint16_t major, uint16_t minor) { |
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.
Not sure placing constexpr
here makes any sense. Could you also consider making this function a static method inside the MetadataBase
class? In order to avoid namespace polution. You'll also need to move the version attribute there, but I believe that also makes sense.
Same about the mejor and minor functions 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.
I chose constexpr
to force the compiler a bit to evaluate the function at compile-time.
But what if I moved those functions outside the namespace?
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.
But what if I moved those functions outside the namespace?
Then you're polluting the global namespace for every other file importing the current one. By "polluting" I mean any other file that intends to use or reimplement a function bearing the same name could run into trouble. "make_version" is not a rare name.
That's why I suggest using a private namespace since this file is the only client (excluding the unit tests, we should not model our source code just to make tests implementation slightly more convenient). Or place it as a static method as mentioned previously, avoids pollution and makes the functions inline
.
blob.end() - metadataIterator - sizeof(blobDataSize)); | ||
|
||
uint32_t metaVersion; | ||
metadataStream.read(reinterpret_cast<char*>(&metaVersion), sizeof(metaVersion)); |
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 check the major version is compatible before attempting to read the rest?
Also, why you saying in PR's description that removing the OV version would break backward compatibility with the old blobs? Did you actually mean forward compatibility? I.e. old plugin + new blob missing this field.
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 major version is already checked inside create_metadata
. This implies that any metadata appended to the blob, has its version as the first field.
And yes, I meant forward compatibility in the PR description. I will update 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.
Then shouldn't we reinforce forward compatibility in the code as well? As it stands, you're throwing if the plugin sees version 1.1 (a future metadata implementation), but you're description suggests this scenario should work.
@@ -29,6 +29,8 @@ ov_add_test_target( | |||
${OpenVINO_SOURCE_DIR}/src/plugins/intel_npu/src/utils/include | |||
${OpenVINO_SOURCE_DIR}/src/plugins/intel_npu/src/plugin/include | |||
${OpenVINO_SOURCE_DIR}/src/plugins/intel_npu/src/al/include | |||
OBJECT_FILES | |||
${OpenVINO_SOURCE_DIR}/src/plugins/intel_npu/src/plugin/src/metadata.cpp |
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.
My CMake skills suck but I gotta ask: why did we need this? Asking cause it looks a bit out-of-the-blue 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.
The source files from intel_npu/src/plugin/src
are not compiled into a static library. That means I cannot add them to the unit tests, without doing some stuff in our root CMakeFiles.
Another solution would imply moving metadata.cpp/hpp
to common
dir which is already compiled into a static lib.
But, the quickest solution was to hardcode the needed source file for my PR.
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.
@pereanub Could you please check everything is fine here?
92c2080
to
448ad5b
Compare
1eb68e2
to
8fb327c
Compare
Signed-off-by: Alexandru Enache <[email protected]>
8fb327c
to
9284582
Compare
Details:
A versioned blob would look like this:
Where Blob data size is necessary when importing the blob to offset jump at Metadata.
Rules of thumb when we need to set a new
Metadata
version:Metadata
, thenMinor
is incremented. This means we maintain backward compatibility with old blobs.Example:
After OV Version we add a new field.
Major
is incremented. This means we break forward compatibility with old blobs.Examples:
Between Minor and OV Version, we add a new field.
OR
We remove OV Version.
Simplified workflow:
Metadata
and verify it against the currently supported one. If the imported blob is incompatible, we reject it and print its OV version (where possible).Tickets: