Skip to content

Eliminate unnecessary DocumentKey->FSTDocumentKey conversions #1507

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

Merged
merged 5 commits into from
Jul 10, 2018

Conversation

var-const
Copy link
Contributor

@var-const var-const commented Jul 9, 2018

This came up while optimizing batches with many mutations. Most of memory allocations and a good chunk of time was due to many unintentional conversions between the C++ DocumentKey and Objective-C FSTDocumentKey. There were two main issues:

  • the conversion from DocumentKey to FSTDocumentKey performed a full copy, instead of taking advantage of the internal optimization in DocumentKey that uses a shared_ptr to make copying cheap;
  • in a few places, instance variables of type FSTDocumentKey were replaced with DocumentKey, but the syntax for comparison wasn't updated. Because the conversion is implicit, the following code converts DocumentKeys to FSTDocumentKeys and then calls the method on them:
DocumentKey foo;
DocumentKey bar;
return [foo isEqualToKey: bar];

This is a follow-up to #1505. I found out that while #1505 improved things a lot, committing more than one batch with many mutations was still unreasonably slow. The profiler showed that most of the time was spent in creating FSTDocumentKeys. To benchmark locally, I used the newly-added performance test which commits a batch with 499 mutations, but created 50 such batches. I tested in release mode and just manually monitored memory usage and execution speed in XCode (on simulator). I got the following stats:

Further optimizations to FSTLocalDocumentsView seem not to be worth it. Even skipping access to LevelDB altogether inside FSTLocalDocumentsView only brings down total time to 19-20 seconds.

const long long memoryUsedAfterCommitMb = getCurrentMemoryUsedInMb();
XCTAssertNotEqual(memoryUsedAfterCommitMb, -1);
const long long memoryDeltaMb = memoryUsedAfterCommitMb - memoryUsedBeforeCommitMb;
// This by its nature cannot be a precise value. Runs on simulator seem to give an increase of
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This function only differs from #1505 in lines 368:370 (the comment and the constant).

Copy link
Contributor

@zxu123 zxu123 left a comment

Choose a reason for hiding this comment

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

Is there still any usage of implicit conversion? If not, could we remove the two functions that does the implicit conversion (each direction) from the .cc file; or otherwise update the implementation there according to the change made in this PR?

const auto errorCode =
task_info(mach_task_self(), MACH_TASK_BASIC_INFO, (task_info_t)&taskInfo, &taskInfoSize);
if (errorCode == KERN_SUCCESS) {
const int bytesInMegabyte = 1000 * 1000;
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: 1024 * 1024? not that much difference, up to you.

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. I guess I used the SI definition.

FIRWriteBatch *batch = [mainDoc.firestore batch];

// >= 500 mutations will be rejected, so use 500-1 mutations
for (int i = 0; i != 500 - 1; ++i) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Could we define a constant i.e. give the literal 500 a name?

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

@wilhuff wilhuff left a comment

Choose a reason for hiding this comment

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

LGTM with a nit


NS_ASSUME_NONNULL_BEGIN

@interface FSTDocumentKey () {
/** The path to the document. */
ResourcePath _path;
DocumentKey _impl;
Copy link
Contributor

Choose a reason for hiding this comment

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

This may be better named _delegate. I'm not a fan of "impl" as a name since it's generic without suggesting how it's the implementation.

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 Author

@var-const var-const left a comment

Choose a reason for hiding this comment

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

Is there still any usage of implicit conversion? If not, could we remove the two functions that does the implicit conversion (each direction) from the .cc file; or otherwise update the implementation there according to the change made in this PR?

Unfortunately, there's still too many usages to tackle in this PR.


NS_ASSUME_NONNULL_BEGIN

@interface FSTDocumentKey () {
/** The path to the document. */
ResourcePath _path;
DocumentKey _impl;
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.

const auto errorCode =
task_info(mach_task_self(), MACH_TASK_BASIC_INFO, (task_info_t)&taskInfo, &taskInfoSize);
if (errorCode == KERN_SUCCESS) {
const int bytesInMegabyte = 1000 * 1000;
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. I guess I used the SI definition.

FIRWriteBatch *batch = [mainDoc.firestore batch];

// >= 500 mutations will be rejected, so use 500-1 mutations
for (int i = 0; i != 500 - 1; ++i) {
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.

@@ -348,7 +348,8 @@ - (void)testReasonableMemoryUsageForLotsOfMutations {
FIRWriteBatch *batch = [mainDoc.firestore batch];

// >= 500 mutations will be rejected, so use 500-1 mutations
for (int i = 0; i != 500 - 1; ++i) {
const int maxMutations = 500 - 1;
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 was actually off by one on this, it's >500 mutations that are rejected, not >=500.

Copy link
Contributor

@wilhuff wilhuff left a comment

Choose a reason for hiding this comment

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

Still LGTM

@var-const var-const merged commit 0f0a1da into master Jul 10, 2018
@var-const var-const deleted the varconst/write-batch-opt-2 branch July 10, 2018 20:08
@firebase firebase locked and limited conversation to collaborators Oct 30, 2019
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.

5 participants