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

Implement std::pmr based memory tracking. #4683

Merged
merged 24 commits into from
Feb 12, 2024

Conversation

davisp
Copy link
Contributor

@davisp davisp commented Jan 29, 2024

This implements memory allocation tracking by using the std::pmr interface introduced in C++17.


TYPE: FEATURE
DESC: Add std::pmr based memory allocation tracking.

Copy link

This pull request has been linked to Shortcut Story #36075: Consolidation: allow experiments for writer profiling..

@davisp davisp requested a review from KiterLuc January 29, 2024 22:49
@davisp davisp force-pushed the pd/sc-36075/implement-pmr-based-memory-tracker branch 5 times, most recently from 7b4785e to f5a0378 Compare February 2, 2024 19:54
@davisp davisp marked this pull request as ready for review February 5, 2024 23:23
@davisp davisp force-pushed the pd/sc-36075/implement-pmr-based-memory-tracker branch 2 times, most recently from 7228426 to 08c12d4 Compare February 6, 2024 17:59
@abigalekim abigalekim force-pushed the pd/sc-36075/implement-pmr-based-memory-tracker branch from 7676d6e to 2b124b5 Compare February 7, 2024 22:52
tiledb/common/pmr.h Outdated Show resolved Hide resolved
tiledb/sm/fragment/fragment_metadata.cc Outdated Show resolved Hide resolved
tiledb/sm/query/readers/sparse_index_reader_base.h Outdated Show resolved Hide resolved
tiledb/sm/query/readers/dense_reader.h Outdated Show resolved Hide resolved
tiledb/sm/query/writers/writer_base.h Show resolved Hide resolved
tiledb/sm/query/strategy_base.h Outdated Show resolved Hide resolved
tiledb/common/memory_tracker.cc Outdated Show resolved Hide resolved
tiledb/common/memory_tracker.cc Show resolved Hide resolved
@KiterLuc KiterLuc changed the title Implement std::pmr based memory tracking Implement std::pmr based memory tracking. Feb 9, 2024
tiledb/common/memory_tracker.cc Outdated Show resolved Hide resolved
tiledb/common/memory_tracker.cc Show resolved Hide resolved
tiledb/common/memory_tracker.cc Show resolved Hide resolved
tiledb/common/memory_tracker.h Show resolved Hide resolved
tiledb/common/memory_tracker.cc Outdated Show resolved Hide resolved
}
}

// Thread start logic mirrored from the ThreadPool.
Copy link
Contributor

Choose a reason for hiding this comment

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

This code is now "copy pasted" 5 times... We should implement a common method for this. Note that I'm opened for a follow up story that we do next iteration.

tiledb/common/memory_tracker.cc Outdated Show resolved Hide resolved
out.open(filename_.value(), std::ios::app);

if (!out) {
LOG_ERROR("Error opening MemoryTrackerReporter file: " + filename_.value());
Copy link
Contributor

Choose a reason for hiding this comment

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

Same comment here as above for throwing.

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 one I'm slightly less inclined to turn into an exception. The first reason is that throwing an exception here just translates into a std::terminate since it's not handled by the thread. Secondly, blowing up because of things changing out from under us at runtime could be a weird behavior.

However, I'll freely admit to writing this in haste with just a goal of getting something working. I think a better approach would be to handle any errors that arise more gracefully and then handle resuming log writes when/if the file becomes writable again. Which is to say, keeping these as log messages but trying to resume writing when we're able is likely better than crashing the entire process.

Copy link
Contributor

Choose a reason for hiding this comment

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

Let’s file a follow up story to clean this up if needed…

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 kept the log messages but added the logic to continually attempt re-opening the log instead of just silently closing the reporter on first error.

tiledb/common/memory_tracker.cc Outdated Show resolved Hide resolved
This change is just to save on typing since we'll be referring to memory
types quite often it just means we can type `MemoryType::RTREE` instead
of `MemoryTracker::MemoryType::RTREE`.
This change helps avoid life time issues of raw pointers to
MemoryTrackers since we now require a MemoryTracker for the entire
lifetime of the tdb::pmr::vector class.
This change adds a new vector type for use when tracking memory usage
within TileDB. This new class is a very thin sub-class of the
std::pmr::vector with all of the constructors and assignment operators
updated to require an allocator argument rather than making them
optional. This will allow us to progressively instrument the TileDB
codebase without making some common mistakes of forgetting to pass the
required memory tracking references when adding them.
For now we'll juse use the default memory resource while we make sure
that translations aren't going to be insanely difficult with the new
memory resource constructor argument.
This commit changes a single member variable on the FragmentMetadata
class to use the new tdb::pmr::vector type and includes all of the
related fixes for updating the new memory tracking requirements.
These particular member variables were chosen because these are the only
variables tracked by the existing MemoryTracker implementation. Once I
implement the counting and switch the implementations it will break a
decent number of unit tests because the new PMR implementations track
all allocations, not just a calculation of what we logically need. In
other words, we're not currently keeping an accurate count of memory,
and getting more accurate counts breaks the existing tests.
This splits up `MemoryType::MIN_MAX_SUM_NULL_COUNT` into four separate
types for better specificity.
This adds a new class that inherits from our `tdb::pmr::memory_resource`
definition. Instances of this class can be passed to `tdb::pmr::vector`
constructos to enable memory tracking at runtime.
This is to distinguish between different instances of MemoryTracker in a
given context. For instance, during consolidation we'll end up with
five. One for the read array, read query, write array, write query, and
one for the consolidator itself.
This allows us to track memory used by instances of Query which include
the bulk of the data in RAM in the form of filtered and unfiltered data
in tiles.
This adds a new MemoryTrackerManager to the ContextResources. All
MemoryTrackers should be created through this API. Eventually I'll
probably make MemoryTracker constructors private so that we force this
condition. However, that would currently make the test changes fairly
verbose so I'm avoiding it for now to reduce the size of this already
decently large diff.
This generates a JSON representation of all the currently active
MemoryTracker instances.
This adds a new MemoryTrackerReporter. If configured, this reporter
starts a background thread that writes a JSON string to the configured
file once a second. To configure the filename, just pass a config value
for `sm.memory.tracker.reporter.filename` when creating the context.
This configuration key is purposefully undocumented so that we aren't
creating a public API contract as the contents of the JSON blobs will
change without warning.
Avoid using iterators while modifying a vector that's being iterated.
I must have read the docs incorrectly on which pointers are invalidated
when calling erase while traversing the iterator. Regardless, using
traditional while loop with indices makes the logic a bit more clean
anyway, so it's an all round win.
A common first issue when adding new memory tracking was to create new
instances of MemoryTracker which should happen very rarely in test code.
If we create new instances outside of a MemoryTrackerManager, these
instances will not be included in the memory reporting.

The correct solution should almost always be to use an existing memory
tracker. Some common trackers to use are on both the Array and Query
classes. In cases where neither of those is appropriate, it may be time
to start considering a new instance of a memory tracker by calling
ContextResources::create_memory_tracker(). Cases where we may need to
create new instances include the usage of instrumented classes outside
of an Array or Query. I.e., when creating a new Array, there's no Array
instance which means we'll have to create a new ARRAY_CREATE (or
some other name) MemoryTracker::Type.
This allows the memory tracking helpers to be used in unit tests.
Spotted by Luc in code review.
@davisp davisp force-pushed the pd/sc-36075/implement-pmr-based-memory-tracker branch from 3d380f0 to d6c74f5 Compare February 12, 2024 18:33
@davisp davisp force-pushed the pd/sc-36075/implement-pmr-based-memory-tracker branch from d6c74f5 to 0249685 Compare February 12, 2024 18:34
@KiterLuc KiterLuc merged commit 695cc84 into dev Feb 12, 2024
63 checks passed
@KiterLuc KiterLuc deleted the pd/sc-36075/implement-pmr-based-memory-tracker branch February 12, 2024 20:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants