-
Notifications
You must be signed in to change notification settings - Fork 6k
Switch PlatformMessages to hold data in Mappings #25867
Conversation
fccfdb9
to
d2bd4f7
Compare
I measured this locally with the unpublished platform channel tests, without removing the copy, just transitioning away from std::vector<uint8_t>. For binary codecs it's a 21% reduction (.193ms -> .1523ms). For standard message codecs it's a 4% reduction (.13048ms -> .12524ms). |
31ed940
to
2d02b81
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.
Only major comment is about the error handling (OOB access and OOMs) and documentation. Other than that, everything else are minor nits. This is great. I have always been uneasy using vectors for bags of bytes.
/// It's templated since void* arithemetic isn't allowed and we want support | ||
/// for `uint8_t` and `char`. | ||
template <typename T> | ||
static MallocMapping Copy(const T* begin, const T* end) { |
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 needs some error handling to ensure we don't read OOB. Specifically, begin > end
and end > begin + size
. The return can be an MallocMapping.
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.
A suggestion: A variant I have found super useful in other projects is the following:
template <class T>
RetVal Copy(const T* buffer, size_t count) {
return Copy(reinterpret_cast<const uint8_t*>(buffer), count * sizeof(buffer));
};
Which is an effective way of saying copy count
objects of type T
tightly packed in this buffer. If you want extra safeguards in place, you can even std::enable_if
the specialization to ensure that T is std::is_trivially_constructible
. That way, folks don't accidentally end up copying non-POD types.
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, I can't assert on size since it doesn't exist here. The reason I chose this signature was just because it matched the code I was migrating from std::vector. In the end I think I moved everything to the Copy signature with length, we could probably delete this but since it was helpful before I thought it might be helpful in the future.
|
||
MallocMapping(const uint8_t* data, size_t size); | ||
|
||
MallocMapping(fml::MallocMapping&& mapping); |
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 there a reason for the preference for this to be allocatable on the stack instead of using unique_ptr to own MallocMappings? Either way, the allocation is going to be on the heap.
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 didn't think about it much beyond just trying to keep it as close as possible to std::vector to make migration easier. I guess this is slightly more efficient since you can read the size without the indirection.
} | ||
|
||
MallocMapping MallocMapping::Copy(const void* begin, size_t length) { | ||
auto result = |
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.
Error handling for OOM cases on malloc failures.
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
~MallocMapping() override; | ||
|
||
/// Copies the data from `begin` to `end`. | ||
/// It's templated since void* arithemetic isn't allowed and we want support |
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 I understand this reasoning. void *
arithmetic is disallowed but the caller can just reinterpret_cast it though right?
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.
Yep, they could. I was trying to make the migration as easy as possible, just added what I made it easiest. The problem was that we use uint8_t everywhere but when you are creating mappings from strings they use int8_t.
return Copy(begin, length); | ||
} | ||
|
||
static MallocMapping Copy(const void* begin, size_t length); |
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.
Docset for the signature. Specifically mentioning the following:
- The error handling to be expected. What happens on OOM and out of bounds access on the source buffer for instance.
- Clarify that size is in bytes.
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.
Suggestion: If this were const uint8_t*
, the length parameter becomes much clearer.
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, I used void* in order to support uint8_t and int8_t. Otherwise I'd have to use templates like above.
fml/mapping.h
Outdated
const uint8_t* GetMapping() const override; | ||
|
||
/// Removes ownership of the data buffer. | ||
const uint8_t* Release(); |
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 don't think this should be const because the caller won't be able to modify it (for freeing mostly) without a const cast. If this object is relinquishing ownerhip of the buffer, why should it have a say in how the caller uses the buffer that it now owns.
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.
[[nodiscard]]
attribute here since ignoring this will be a leak.
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 no discard. Yea, you're right about the constness, remove it.
fml/mapping.cc
Outdated
} | ||
|
||
MallocMapping::~MallocMapping() { | ||
if (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.
This is not necessary as ::free(nullptr)
is safe. In fact, I think clang-tidy even has a fix-it for this sort of deletion.
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.
Looked it up, its for delete
https://clang.llvm.org/extra/clang-tidy/checks/readability-delete-null-pointer.html. Same stuff.
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.
nice, done, thanks
@chinmaygarde friendly ping. I have a few more PR's that this depends on. |
This is good to land once we get the LGTM, the performance tests are running here: https://flutter-flutter-perf.skia.org/e/?queries=test%3Dmac_ios_platform_channels_benchmarks_ios |
* 7adb8de Roll Skia from 6b719c25cade to ca9f6a855071 (3 revisions) (flutter/engine#26127) * a1062aa [uwptool] Support lookup of full package name (flutter/engine#26121) * 01ce6a1 Get android_lint deps using gclient rather than pub. (flutter/engine#26106) * cba6c1e Use cached CanvasKit instance if available (flutter/engine#26059) * b0bb8ea Switch PlatformMessages to hold data in Mappings (flutter/engine#25867)
Switch PlatformMessages to having their data in std::vector to fml::Mapping so that we can steal ownership of the data and have more efficient allocation / deallocation of the data.
On iOS this had about a 20% increase in performance tests for 14k binary platform messages. The savings seem to come from the removal of the
~std::vector<uint8_t>
calls.Do not merge this until the performance tests are running.
This PR depends on #25860
Next PR: #25988
Issue: flutter/flutter#81559
Pre-launch Checklist
writing and running engine tests.
///
).If you need help, consider asking for advice on the #hackers-new channel on Discord.