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

Improve join index memory accounting #15672

Merged
merged 5 commits into from
Jan 18, 2023

Conversation

arhimondr
Copy link
Contributor

Description

Additional context and related issues

Release notes

(X) This is not user-visible or docs only and no release notes are required.
( ) Release notes are required, please propose a release note for me.
( ) Release notes are required, with the following suggested text:

# Section
* Fix some things. ({issue}`issuenumber`)

@arhimondr arhimondr requested review from losipiuk and sopel39 January 11, 2023 17:09
@cla-bot cla-bot bot added the cla-signed label Jan 11, 2023
@arhimondr arhimondr changed the title Improve join index memory accounting [WIP] Improve join index memory accounting Jan 11, 2023
@arhimondr
Copy link
Contributor Author

Still WIP

@@ -79,9 +82,29 @@ public JoinHashSupplier(
positionLinksFactoryBuilder = ArrayPositionLinks.builder(addresses.size());
}

this.pages = channelsToPages(channels);
long additionalPagesRetainedSizeInBytes = 0;
// Currently PageIndex is retained by the HashBuilderOperator after JoinHash is created.
Copy link
Member

Choose a reason for hiding this comment

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

Is or is-not. If it is retained then why do we need to account for this memory once again?

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 is rather convoluted. HashBuilderOperator retains PageIndex. When PageIndex is being built memory usage is set to PageIndex#getEstimatedSize. After LookupSource (JoinHash) is created memory usage of HashBuilderOperator is set to JoinHash#getInMemorySizeInBytes while a reference to PageIndex is still retained. I think the assumption is that the JoinHash retains same data what PageIndex does, and it had been (almost) true up until the introduction of positionCounts. However the overhead of pages created for JoinHash have never been accounted, that's also what I'm trying to accommodate here.

It feels like ideally it has to be refactored:

  • Once JoinHash is created PageIndex has to be released. This is problematic with spilling, but should be doable in a version that doesn't support spilling
  • JoinHash ideally has to be refactored to avoid this duality of having List<Page> in addition to List<List<Block>>. However it is not a straightforward change.

this.pages = channelsToPages(channels);
long additionalPagesRetainedSizeInBytes = 0;
// Currently PageIndex is retained by the HashBuilderOperator after JoinHash is created.
// JoinHash has to account for all data structures retained by PageIndex as after JoinHash is created memory retained by PageIndex is no longer accounted for.
Copy link
Member

Choose a reason for hiding this comment

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

Also can we just see how much much memory PageIndex consumed instead recomputing piece by piece here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

PageIndex stores data as a List<List<Block>>. However JoinHash nees the same data in a form of List<Page>, so we have to account the overhead of Page wrappers, but avoid accounting the size of blocks twice. Ideally it would be great to refactor and only use data in one form.

@arhimondr arhimondr force-pushed the fix-join-index-memory-accounting branch from 4d635cf to f6dd7fa Compare January 11, 2023 19:34
@@ -306,6 +306,11 @@ private void finishInput()
return;
}

ListenableFuture<Void> reserved = localUserMemoryContext.setBytes(index.getEstimatedLookupSourceSizeInBytes(hashArraySizeSupplier));
if (!reserved.isDone()) {
// Yield when not enough memory is available to proceed, finish is expected to be called again when some memory is freed
Copy link
Member

Choose a reason for hiding this comment

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

That seems to be awfully dependent on Driver detail. Could we make operator return blocked future instead.

Please add tests also

Copy link
Contributor Author

@arhimondr arhimondr Jan 12, 2023

Choose a reason for hiding this comment

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

That seems to be awfully dependent on Driver detail. Could we make operator return blocked future instead.

It is generally true that currently it is responsibility of the system to "park" Drivers when the system is low on memory. For example if the HashAggregationOperator pushes the memory pool over the limit we currently don't explicitly block the operator, instead we simply update the memory utilization and expect the engine not to proceed if the system is low on memory.

Please add tests also

Yeah, still working on it.

@arhimondr arhimondr force-pushed the fix-join-index-memory-accounting branch from f6dd7fa to 8e0cd24 Compare January 12, 2023 22:05
@arhimondr arhimondr changed the title [WIP] Improve join index memory accounting Improve join index memory accounting Jan 12, 2023
@arhimondr
Copy link
Contributor Author

Ready for review

@arhimondr arhimondr force-pushed the fix-join-index-memory-accounting branch from 8e0cd24 to 1ca769a Compare January 15, 2023 20:37
@@ -616,4 +618,12 @@ public Page computeNext()
}
};
}

public long getEstimatedLookupSourceSizeInBytes(HashArraySizeSupplier hashArraySizeSupplier)
Copy link
Member

Choose a reason for hiding this comment

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

Estimate will depend on which PagesHash implementation is used and is also subject to change in the future.

You could pass LocalMemoryContext to io.trino.operator.PagesIndex#createLookupSourceSupplier and make createLookupSourceSupplier return a future.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Discussed offline. Decided to extract helper methods for estimating every part of the JoinHash

@@ -79,7 +83,20 @@ public JoinHashSupplier(
positionLinksFactoryBuilder = ArrayPositionLinks.builder(addresses.size());
}

long additionalPagesRetainedSizeInBytes = 0;
// Currently PageIndex is retained by the HashBuilderOperator after JoinHash is created.
// JoinHash has to account for all data structures retained by PageIndex as after JoinHash is created memory retained by PageIndex is no longer accounted for.
Copy link
Member

@sopel39 sopel39 Jan 16, 2023

Choose a reason for hiding this comment

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

for all data structures retained by PageIndex as after JoinHash

Where PagesIndex is still referenced? Can we simplify it (composite retained memory computation or use memory context) rather than feeding back special parameters?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Discussed offline. Decided to set PageIndex to null once it is no longer needed.

@arhimondr arhimondr force-pushed the fix-join-index-memory-accounting branch from 1ca769a to 36c42eb Compare January 16, 2023 22:09
@arhimondr
Copy link
Contributor Author

Updated

@arhimondr arhimondr force-pushed the fix-join-index-memory-accounting branch from 36c42eb to 31b33c1 Compare January 17, 2023 03:02
@sopel39 sopel39 requested a review from gaurav8297 January 17, 2023 15:38
@@ -321,14 +323,13 @@ private void disposeLookupSourceIfRequested()
return;
}

index.clear();
Copy link
Member

Choose a reason for hiding this comment

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

check state index is null?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

close set's it to null unconditionally (as well as lookupSourceSupplier). Actually let me remove the lookupSourceSupplier = null as well

@arhimondr arhimondr force-pushed the fix-join-index-memory-accounting branch from 31b33c1 to fabf99c Compare January 18, 2023 00:41
@arhimondr
Copy link
Contributor Author

@sopel39 Updated

@arhimondr arhimondr force-pushed the fix-join-index-memory-accounting branch from fabf99c to 702d868 Compare January 18, 2023 04:00
@@ -322,7 +322,6 @@ private void disposeLookupSourceIfRequested()
return;
}

lookupSourceSupplier = null;
Copy link
Member

Choose a reason for hiding this comment

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

squash?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It is pre-existing

@arhimondr arhimondr merged commit 9d6764d into trinodb:master Jan 18, 2023
@arhimondr arhimondr deleted the fix-join-index-memory-accounting branch January 18, 2023 15:36
@github-actions github-actions bot added this to the 406 milestone Jan 18, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

Successfully merging this pull request may close these issues.

3 participants