Skip to content

Conversation

@edsavage
Copy link
Contributor

@edsavage edsavage commented May 28, 2024

The current code uses the monotonic resource allocator, for allocating memory to boost::json objects, which allocates memory in ever increasing chunks, which can lead to over allocation. The image below shows a typical series of memory allocations when using the monotonic resource allocator

Screenshot 2024-05-29 at 15 07 42

The other disadvantage of the monotonic resource allocator is that no deallocations are performed until the resource allocator is destroyed - hence the name monotonic as resource allocations can only increase during its lifetime.

These factors make the choice of the monotonic resource allocator unsuitable for its current use.

This PR introduces a very simplistic custom allocator that allocates and deallocates individual objects upon request using standard operator ::new and ::delete. This gives a much better experience as only as much memory is allocated at any point in time as absolutely needs to be, and gives a much more predictable memory profile

Screenshot 2024-05-29 at 15 06 02

On small data sets this change appears performant, but I do think it would be wise to run the QA tests against this PR, before merging.

@edsavage edsavage added the ci:run-qa-tests Run a subset of the QA tests label May 29, 2024
@edsavage edsavage changed the title [ML] Experiment with different allocator types [ML] Use custom Boost::JSON allocator May 29, 2024
@edsavage edsavage added v8.14.0 and removed v8.14.1 labels May 29, 2024
@edsavage edsavage marked this pull request as ready for review May 29, 2024 03:36
@edsavage
Copy link
Contributor Author

buildkite run_qa_tests

1 similar comment
@wwang500
Copy link

buildkite run_qa_tests

Copy link
Contributor

@valeriy42 valeriy42 left a comment

Choose a reason for hiding this comment

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

Good work on figuring out and changing the allocator behavior. I think we need to report the allocator memory usage as part of the memory_stats since it may be considerable.

When considering whether or not to update the model, we use the following code:

std::size_t CResourceMonitor::allocationLimit() const {
    return this->highLimit() - std::min(this->highLimit(), this->totalMemory());
}

Hence, we need to be transparent why the job gets into the hard_limit state while model_bytes is lower.

//! from the CResourceMonitor via a callback
void reportMemoryUsage(const model::CResourceMonitor::SModelSizeStats& modelSizeStats);

std::size_t getAllocatorMemUsage() const;
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you please add a short documentation?

@edsavage
Copy link
Contributor Author

report the allocator memory usage as part of the memory_stats

Just to clarify @valeriy42 , by memory_stats do you mean the Model size stats (as reported in the counts tab in the AD job results in Kibana)? i.e. include the JSON allocator mem usage in model::CResourceMonitor::SModelSizeStats?

edsavage added 3 commits June 12, 2024 16:26
* Add JSON allocator memory usage to the reported model memory stats
@valeriy42
Copy link
Contributor

report the allocator memory usage as part of the memory_stats

Just to clarify @valeriy42 , by memory_stats do you mean the Model size stats (as reported in the counts tab in the AD job results in Kibana)? i.e. include the JSON allocator mem usage in model::CResourceMonitor::SModelSizeStats?

Exactly. Sorry for mixing up memory_stats and model_size_stats.

@edsavage
Copy link
Contributor Author

buildkite build this

Copy link
Contributor

@valeriy42 valeriy42 left a comment

Choose a reason for hiding this comment

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

LGTM.

const std::string CATEGORIZER_STATS{"categorizer_stats"};
const std::string PARTITION_FIELD_NAME{"partition_field_name"};
const std::string PARTITION_FIELD_VALUE{"partition_field_value"};
const std::string JSON_MEMORY_ALLOCATOR_BYTES("json_memory_allocator_bytes");
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: I am wondering if this name is not too specific. Maybe output_memory_allocator_bytes?

* rename jsonMemoryAllocator -> outputMemoryAllocator
@edsavage edsavage merged commit 7d08ac6 into elastic:main Jun 18, 2024
edsavage added a commit to edsavage/ml-cpp that referenced this pull request Jun 18, 2024
The current code uses the monotonic resource allocator, for allocating memory to boost::json objects, which allocates memory in ever increasing chunks, which can lead to over allocation.

The other disadvantage of the monotonic resource allocator is that no deallocations are performed until the resource allocator is destroyed - hence the name monotonic as resource allocations can only increase during its lifetime.

These factors make the choice of the monotonic resource allocator unsuitable for its current use.

This PR introduces a very simplistic custom allocator that allocates and deallocates individual objects upon request using standard operator ::new and ::delete. This gives a much better experience as only as much memory is allocated at any point in time as absolutely needs to be, and gives a much more predictable memory profile
edsavage added a commit that referenced this pull request Jun 18, 2024
The current code uses the monotonic resource allocator, for allocating memory to boost::json objects, which allocates memory in ever increasing chunks, which can lead to over allocation.

The other disadvantage of the monotonic resource allocator is that no deallocations are performed until the resource allocator is destroyed - hence the name monotonic as resource allocations can only increase during its lifetime.

These factors make the choice of the monotonic resource allocator unsuitable for its current use.

This PR introduces a very simplistic custom allocator that allocates and deallocates individual objects upon request using standard operator ::new and ::delete. This gives a much better experience as only as much memory is allocated at any point in time as absolutely needs to be, and gives a much more predictable memory profile

Backports #2674
@DaveCTurner
Copy link

Sorry to say that elastic/elasticsearch#109833 thoroughly breaks the ES wire protocol, I'm going to have to revert it to fix the ES build. I guess that means something needs to be reverted here too, but I'm not qualified to address that.

@edsavage edsavage deleted the boost_json_custom_allocator branch September 19, 2024 01:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants