Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Conversation

gaaclarke
Copy link
Member

@gaaclarke gaaclarke commented Apr 30, 2021

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

  • I read the Contributor Guide and followed the process outlined there for submitting PRs.
  • I read the Tree Hygiene wiki page, which explains my responsibilities.
  • I read and followed the Flutter Style Guide and the C++, Objective-C, Java style guides.
  • I listed at least one issue that this PR fixes in the description above.
  • I added new tests to check the change I am making or feature I am adding, or Hixie said the PR is test-exempt. See testing the engine for instructions on
    writing and running engine tests.
  • I updated/added relevant documentation (doc comments with ///).
  • I signed the CLA.
  • All existing and new tests are passing.
  • The reviewer has submitted any presubmit flakes in this PR using the engine presubmit flakes form before re-triggering the failure.

If you need help, consider asking for advice on the #hackers-new channel on Discord.

@gaaclarke gaaclarke changed the title Mapping buffers Switch PlatformMessages to hold data in NonOwnedMapping Apr 30, 2021
@google-cla google-cla bot added the cla: yes label Apr 30, 2021
@gaaclarke gaaclarke changed the title Switch PlatformMessages to hold data in NonOwnedMapping Switch PlatformMessages to hold data in Mappings May 3, 2021
@gaaclarke gaaclarke force-pushed the mapping-buffers branch 8 times, most recently from fccfdb9 to d2bd4f7 Compare May 3, 2021 22:46
@gaaclarke
Copy link
Member Author

gaaclarke commented May 4, 2021

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

@gaaclarke gaaclarke force-pushed the mapping-buffers branch 2 times, most recently from 31ed940 to 2d02b81 Compare May 6, 2021 20:58
@gaaclarke gaaclarke marked this pull request as ready for review May 7, 2021 16:25
@gaaclarke gaaclarke requested a review from chinmaygarde May 7, 2021 16:25
Copy link
Member

@chinmaygarde chinmaygarde left a 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) {
Copy link
Member

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.

Copy link
Member

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.

Copy link
Member Author

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

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.

Copy link
Member Author

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 =
Copy link
Member

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.

Copy link
Member Author

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

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?

Copy link
Member Author

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

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.

Copy link
Member

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.

Copy link
Member Author

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

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.

Copy link
Member

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.

Copy link
Member Author

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

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.

Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Member Author

Choose a reason for hiding this comment

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

nice, done, thanks

@gaaclarke gaaclarke requested a review from chinmaygarde May 7, 2021 21:23
@gaaclarke
Copy link
Member Author

@chinmaygarde friendly ping. I have a few more PR's that this depends on.

@gaaclarke
Copy link
Member Author

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

@gaaclarke gaaclarke merged commit b0bb8ea into flutter:master May 13, 2021
engine-flutter-autoroll added a commit to engine-flutter-autoroll/flutter that referenced this pull request May 13, 2021
cbracken pushed a commit to flutter/flutter that referenced this pull request May 13, 2021
* 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)
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants