Skip to content
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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

alexandruenache1111
Copy link
Contributor

@alexandruenache1111 alexandruenache1111 commented Oct 21, 2024

Details:

A versioned blob would look like this:
blob-layout

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:

  • if we add fields at the end of current Metadata, then Minor is incremented. This means we maintain backward compatibility with old blobs.

Example:
After OV Version we add a new field.

  • if we remove fields or add fields anywhere but at end, then 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:

  • at export: we append the versioning stuff at the end of the blob
  • at import: we extract the stored 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:

  • E-135371

@github-actions github-actions bot added the category: NPU OpenVINO NPU plugin label Oct 21, 2024
@alexandruenache1111 alexandruenache1111 self-assigned this Oct 25, 2024
@alexandruenache1111 alexandruenache1111 marked this pull request as ready for review October 25, 2024 09:50
@alexandruenache1111 alexandruenache1111 requested review from a team as code owners October 25, 2024 09:50
@alexandruenache1111 alexandruenache1111 marked this pull request as draft November 11, 2024 17:12
@alexandruenache1111 alexandruenache1111 force-pushed the blob_stream_commit branch 2 times, most recently from aaba4a6 to 421d071 Compare November 12, 2024 06:45
@alexandruenache1111 alexandruenache1111 marked this pull request as ready for review November 12, 2024 06:46
@github-actions github-actions bot added the category: build OpenVINO cmake script / infra label Nov 28, 2024
Copy link
Contributor

@StefaniaHergane StefaniaHergane left a 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().

src/plugins/intel_npu/src/plugin/src/metadata.cpp Outdated Show resolved Hide resolved
@sbutnari
Copy link
Contributor

build_jenkins


virtual void set_version(uint32_t newVersion) = 0;

virtual void set_ov_version(OpenvinoVersion& newVersion) = 0;
Copy link
Contributor

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.

Copy link
Contributor Author

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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No resize before this?

Copy link
Contributor Author

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.

Copy link
Contributor Author

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.
Copy link
Contributor

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).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Copy link
Contributor

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.

src/plugins/intel_npu/src/plugin/src/metadata.cpp Outdated Show resolved Hide resolved
src/plugins/intel_npu/src/plugin/include/metadata.hpp Outdated Show resolved Hide resolved
}

void OpenvinoVersion::read(std::istream& stream) {
stream.read(reinterpret_cast<char*>(&size), sizeof(size));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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.

Copy link
Contributor Author

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.

Copy link
Contributor

@razvanapetroaie razvanapetroaie Dec 10, 2024

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.

Copy link
Contributor

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.

src/plugins/intel_npu/src/plugin/src/metadata.cpp Outdated Show resolved Hide resolved
return storedMeta;
}

void Metadata<METADATA_VERSION_1_0>::set_version(uint32_t newVersion) {
Copy link
Contributor

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?

Copy link
Contributor Author

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.

Copy link
Contributor

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) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above.

Copy link
Contributor Author

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) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure placing constexprhere 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.

Copy link
Contributor Author

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?

Copy link
Contributor

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));
Copy link
Contributor

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.

Copy link
Contributor Author

@alexandruenache1111 alexandruenache1111 Dec 9, 2024

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.

Copy link
Contributor

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
Copy link
Contributor

@razvanapetroaie razvanapetroaie Dec 5, 2024

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.

Copy link
Contributor Author

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.

Copy link
Contributor

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?

@alexandruenache1111 alexandruenache1111 requested review from a team as code owners December 6, 2024 08:10
@github-actions github-actions bot added category: tools OpenVINO C++ / Python tools category: OVC OVC tool labels Dec 6, 2024
@alexandruenache1111 alexandruenache1111 removed the request for review from a team December 6, 2024 08:12
@alexandruenache1111 alexandruenache1111 removed the request for review from a team December 6, 2024 08:12
@github-actions github-actions bot removed category: tools OpenVINO C++ / Python tools category: OVC OVC tool labels Dec 6, 2024
Signed-off-by: Alexandru Enache <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
category: build OpenVINO cmake script / infra category: NPU OpenVINO NPU plugin
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants