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

The AllocationTick threshold is computed by a Poisson process with a 100 KB mean. #85750

Closed
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 44 additions & 13 deletions src/coreclr/gc/gc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
//

#include "gcpriv.h"
#include <math.h>
#include <time.h>

#if defined(TARGET_AMD64) && defined(TARGET_WINDOWS)
#define USE_VXSORT
Expand Down Expand Up @@ -1892,7 +1894,7 @@ size_t align_on_segment_hard_limit (size_t add)

#endif //SERVER_GC

const size_t etw_allocation_tick = 100*1024;
const size_t etw_allocation_tick_mean = 100*1024;

const size_t low_latency_alloc = 256*1024;

Expand Down Expand Up @@ -2480,6 +2482,7 @@ uint8_t* gc_heap::last_gen1_pin_end;
gen_to_condemn_tuning gc_heap::gen_to_condemn_reasons;

size_t gc_heap::etw_allocation_running_amount[total_oh_count];
size_t gc_heap::etw_allocation_running_threshold[total_oh_count];

uint64_t gc_heap::total_alloc_bytes_soh = 0;

Expand Down Expand Up @@ -14424,6 +14427,10 @@ gc_heap::init_gc_heap (int h_number)
#endif //MULTIPLE_HEAPS

memset (etw_allocation_running_amount, 0, sizeof (etw_allocation_running_amount));
for (int i = 0; i < total_oh_count; i++)
{
etw_allocation_running_threshold[i] = etw_allocation_tick_mean;
}
memset (allocated_since_last_gc, 0, sizeof (allocated_since_last_gc));
memset (&oom_info, 0, sizeof (oom_info));
memset (&fgm_result, 0, sizeof (fgm_result));
Expand Down Expand Up @@ -18009,6 +18016,23 @@ void gc_heap::trigger_gc_for_alloc (int gen_number, gc_reason gr,
#endif //BACKGROUND_GC
}

// The code of the helper function is a replicate of CRT rand() implementation.
inline
int FastRNG(int iMaxValue)
{
static BOOL bisRandInit = FALSE;
static int lHoldrand = 1L;

if (!bisRandInit)
{
lHoldrand = (int)time(NULL);
bisRandInit = TRUE;
}
int randValue = (((lHoldrand = lHoldrand * 214013L + 2531011L) >> 16) & 0x7fff);
return randValue % iMaxValue;
}


inline
bool gc_heap::update_alloc_info (int gen_number, size_t allocated_size, size_t* etw_allocation_amount)
{
Expand All @@ -18018,11 +18042,26 @@ bool gc_heap::update_alloc_info (int gen_number, size_t allocated_size, size_t*

size_t& etw_allocated = etw_allocation_running_amount[oh_index];
etw_allocated += allocated_size;
if (etw_allocated > etw_allocation_tick)

size_t& etw_threshold = etw_allocation_running_threshold[oh_index];
if (etw_allocated > etw_threshold)
{
*etw_allocation_amount = etw_allocated;
exceeded_p = true;
etw_allocated = 0;

// avoid computing if not needed
#ifdef FEATURE_EVENT_TRACE
#ifdef FEATURE_NATIVEAOT
if (EVENT_ENABLED(GCAllocationTick_V1))
#else
if (EVENT_ENABLED(GCAllocationTick_V4))
#endif
{
// compute the next threshold based on a Poisson process with a etw_allocation_tick_mean average
Copy link
Member

Choose a reason for hiding this comment

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

many of us aren't greatly familiar with statistics so making this explanation not so vague would be helpful. instead of saying "based on a Possion process" it'd be much more helpful to start with something like "we are treating this as a Possion process because each sample we take has no influence on any other sample. the samples are exponentially distributed in a Possion process, meaning that the possibility of the next sample happening is calculated by (1 - e^(-lambda*x)). and then explain what lambda and x would be in this particular context so the readers know how the formula you are using came to be.

also -ln (1 - uniformly_random_number_between_0_and_1), is the same as -ln (uniformly_random_number_between_0_and_1). so I don't think you need the 1 - part.

can you please show the results of running this on some workloads where this is much better compared to the current implementation? also have you tried with just a uniformly random distribution instead of an exponential distribution?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

many of us aren't greatly familiar with statistics so making this explanation not so vague would be helpful. instead of saying "based on a Possion process" it'd be much more helpful to start with something like "we are treating this as a Possion process because each sample we take has no influence on any other sample. the samples are exponentially distributed in a Possion process, meaning that the possibility of the next sample happening is calculated by (1 - e^(-lambda*x)). and then explain what lambda and x would be in this particular context so the readers know how the formula you are using came to be.

I updated the description accordingly with also additional information about the upscaling formula

Copy link
Contributor Author

Choose a reason for hiding this comment

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

can you please show the results of running this on some workloads where this is much better compared to the current implementation? also have you tried with just a uniformly random distribution instead of an exponential distribution?

I'm currently simulating the results based on a web application for which I'm recording ALL allocations using ICorProfilerCallback::ObjectAllocated() and check against the sampled then upscaled sizes. The variance of the results shows almost random results for fixed threshold, much better for variable threshold as in the first commit and a little better if sampling could happen within allocation context.
Since the recorder is available in the Datadog profiler only, it will be complicated to generate the corresponding .balloc files (i.e. list of allocations - type+size) used by the simulation to show result on any application. BTW, is there any sample application that you would like to see used as example?

Copy link
Contributor Author

@chrisnas chrisnas May 7, 2023

Choose a reason for hiding this comment

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

also -ln (1 - uniformly_random_number_between_0_and_1), is the same as -ln (uniformly_random_number_between_0_and_1). so I don't think you need the 1 - part.

This sticks to the mathematical way to derive the formula. Since the result should be the same, I would recommend to keep it as it is but no problem to change it.

etw_threshold = (size_t)(-log(1 - ((double)FastRNG(RAND_MAX)/(double)RAND_MAX)) * etw_allocation_tick_mean) + 1;
}
#endif
}

return exceeded_p;
Expand Down Expand Up @@ -46228,22 +46267,14 @@ void StressHeapDummy ();
// the test/application run by CLR is enabling any FPU exceptions.
// We want to avoid any unexpected exception coming from stress
// infrastructure, so CLRRandom is not an option.
// The code below is a replicate of CRT rand() implementation.
// The code of the helper function is a replicate of CRT rand() implementation.
// Using CRT rand() is not an option because we will interfere with the user application
// that may also use it.
int StressRNG(int iMaxValue)
{
static BOOL bisRandInit = FALSE;
static int lHoldrand = 1L;

if (!bisRandInit)
{
lHoldrand = (int)time(NULL);
bisRandInit = TRUE;
}
int randValue = (((lHoldrand = lHoldrand * 214013L + 2531011L) >> 16) & 0x7fff);
return randValue % iMaxValue;
return FastRNG(iMaxValue);
}

#endif // STRESS_HEAP
#endif // !FEATURE_NATIVEAOT

Expand Down
1 change: 1 addition & 0 deletions src/coreclr/gc/gcpriv.h
Original file line number Diff line number Diff line change
Expand Up @@ -3854,6 +3854,7 @@ class gc_heap

PER_HEAP_FIELD_DIAG_ONLY gen_to_condemn_tuning gen_to_condemn_reasons;
PER_HEAP_FIELD_DIAG_ONLY size_t etw_allocation_running_amount[total_oh_count];
PER_HEAP_FIELD_DIAG_ONLY size_t etw_allocation_running_threshold[total_oh_count];
PER_HEAP_FIELD_DIAG_ONLY uint64_t total_alloc_bytes_soh;
PER_HEAP_FIELD_DIAG_ONLY uint64_t total_alloc_bytes_uoh;

Expand Down