Skip to content

Commit 0d04df6

Browse files
author
Nathan Ricci
authored
[MONO] Implement GC.AddMemoryPressure in Mono (#82121)
Implementation of AddMemoryPressure in Mono
1 parent 473e278 commit 0d04df6

File tree

9 files changed

+142
-14
lines changed

9 files changed

+142
-14
lines changed

src/mono/System.Private.CoreLib/src/System/GC.Mono.cs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,10 @@ public static partial class GC
3636
private static extern void InternalCollect(int generation);
3737

3838
[MethodImplAttribute(MethodImplOptions.InternalCall)]
39-
private static extern void RecordPressure(long bytesAllocated);
39+
private static extern void AddPressure(ulong bytesAllocated);
40+
41+
[MethodImplAttribute(MethodImplOptions.InternalCall)]
42+
private static extern void RemovePressure(ulong bytesRemoved);
4043

4144
// TODO: Move following to ConditionalWeakTable
4245
[MethodImplAttribute(MethodImplOptions.InternalCall)]
@@ -60,7 +63,7 @@ public static void AddMemoryPressure(long bytesAllocated)
6063
{
6164
ArgumentOutOfRangeException.ThrowIfGreaterThan(bytesAllocated, int.MaxValue);
6265
}
63-
RecordPressure(bytesAllocated);
66+
AddPressure((ulong)bytesAllocated);
6467
}
6568

6669
public static void RemoveMemoryPressure(long bytesAllocated)
@@ -70,7 +73,7 @@ public static void RemoveMemoryPressure(long bytesAllocated)
7073
{
7174
ArgumentOutOfRangeException.ThrowIfGreaterThan(bytesAllocated, int.MaxValue);
7275
}
73-
RecordPressure(-bytesAllocated);
76+
RemovePressure((ulong)bytesAllocated);
7477
}
7578

7679
[MethodImplAttribute(MethodImplOptions.InternalCall)]

src/mono/mono/metadata/boehm-gc.c

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -311,14 +311,20 @@ mono_restart_world (MonoThreadInfoFlags flags)
311311
* are indirectly referenced by managed objects (for example unmanaged
312312
* memory holding image or other binary data).
313313
* This is a hint only to the garbage collector algorithm.
314-
* Note that negative amounts of p value will decrease the memory
315-
* pressure.
314+
* `value` must be greater than or equal to 0.
315+
* To remove pressure, use `mono_gc_remove_memory_pressure`.
316316
*/
317317
void
318-
mono_gc_add_memory_pressure (gint64 value)
318+
mono_gc_add_memory_pressure (guint64 value)
319319
{
320320
}
321321

322+
void
323+
mono_gc_remove_memory_pressure (guint64 value)
324+
{
325+
}
326+
327+
322328
/**
323329
* mono_gc_get_used_size:
324330
*

src/mono/mono/metadata/gc-internals.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,9 @@ mono_gc_register_object_with_weak_fields (MonoObjectHandle obj);
169169
typedef void (*MonoFinalizationProc)(gpointer, gpointer); // same as SGenFinalizationProc, GC_finalization_proc
170170

171171
void mono_gc_register_for_finalization (MonoObject *obj, MonoFinalizationProc user_data);
172-
void mono_gc_add_memory_pressure (gint64 value);
172+
void mono_gc_add_memory_pressure (guint64 value);
173+
void mono_gc_remove_memory_pressure (guint64 value);
174+
173175
MONO_API int mono_gc_register_root (char *start, size_t size, MonoGCDescriptor descr, MonoGCRootSource source, void *key, const char *msg);
174176
MONO_COMPONENT_API void mono_gc_deregister_root (char* addr);
175177
void mono_gc_finalize_domain (MonoDomain *domain);

src/mono/mono/metadata/icall-decl.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,9 @@ ICALL_EXPORT void ves_icall_System_Array_SetGenericValue_icall (MonoObjectHandle
131131

132132
ICALL_EXPORT void ves_icall_System_Environment_Exit (int);
133133
ICALL_EXPORT void ves_icall_System_GC_InternalCollect (int generation);
134-
ICALL_EXPORT void ves_icall_System_GC_RecordPressure (gint64);
134+
ICALL_EXPORT void ves_icall_System_GC_AddPressure (guint64);
135+
ICALL_EXPORT void ves_icall_System_GC_RemovePressure (guint64);
136+
135137
ICALL_EXPORT void ves_icall_System_GC_WaitForPendingFinalizers (void);
136138
ICALL_EXPORT void ves_icall_System_GC_GetGCMemoryInfo (gint64*, gint64*, gint64*, gint64*, gint64*, gint64*);
137139

src/mono/mono/metadata/icall-def.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,8 @@ NOHANDLES(ICALL(ENV_15, "get_TickCount", ves_icall_System_Environment_get_TickCo
206206
NOHANDLES(ICALL(ENV_15a, "get_TickCount64", ves_icall_System_Environment_get_TickCount64))
207207
NOHANDLES(ICALL(ENV_20, "set_ExitCode", mono_environment_exitcode_set))
208208

209-
ICALL_TYPE(GC, "System.GC", GC_13)
209+
ICALL_TYPE(GC, "System.GC", GC_4a)
210+
NOHANDLES(ICALL(GC_4a, "AddPressure", ves_icall_System_GC_AddPressure))
210211
HANDLES(GC_13, "AllocPinnedArray", ves_icall_System_GC_AllocPinnedArray, MonoArray, 2, (MonoReflectionType, gint32))
211212
NOHANDLES(ICALL(GC_10, "GetAllocatedBytesForCurrentThread", ves_icall_System_GC_GetAllocatedBytesForCurrentThread))
212213
NOHANDLES(ICALL(GC_0, "GetCollectionCount", ves_icall_System_GC_GetCollectionCount))
@@ -215,7 +216,7 @@ NOHANDLES(ICALL(GC_0b, "GetMaxGeneration", ves_icall_System_GC_GetMaxGeneration)
215216
HANDLES(GC_11, "GetTotalAllocatedBytes", ves_icall_System_GC_GetTotalAllocatedBytes, guint64, 1, (MonoBoolean))
216217
NOHANDLES(ICALL(GC_1, "GetTotalMemory", ves_icall_System_GC_GetTotalMemory))
217218
NOHANDLES(ICALL(GC_2, "InternalCollect", ves_icall_System_GC_InternalCollect))
218-
NOHANDLES(ICALL(GC_4a, "RecordPressure", ves_icall_System_GC_RecordPressure))
219+
NOHANDLES(ICALL(GC_5, "RemovePressure", ves_icall_System_GC_RemovePressure))
219220
NOHANDLES(ICALL(GC_6, "WaitForPendingFinalizers", ves_icall_System_GC_WaitForPendingFinalizers))
220221
NOHANDLES(ICALL(GC_12, "_GetGCMemoryInfo", ves_icall_System_GC_GetGCMemoryInfo))
221222
HANDLES(GC_6b, "_ReRegisterForFinalize", ves_icall_System_GC_ReRegisterForFinalize, void, 1, (MonoObject))

src/mono/mono/metadata/icall.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7286,11 +7286,17 @@ ves_icall_System_GC_GetTotalAllocatedBytes (MonoBoolean precise, MonoError* erro
72867286
}
72877287

72887288
void
7289-
ves_icall_System_GC_RecordPressure (gint64 value)
7289+
ves_icall_System_GC_AddPressure (guint64 value)
72907290
{
72917291
mono_gc_add_memory_pressure (value);
72927292
}
72937293

7294+
void
7295+
ves_icall_System_GC_RemovePressure (guint64 value)
7296+
{
7297+
mono_gc_remove_memory_pressure (value);
7298+
}
7299+
72947300
MonoBoolean
72957301
ves_icall_System_Threading_Thread_YieldInternal (void)
72967302
{

src/mono/mono/metadata/sgen-mono.c

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2867,11 +2867,16 @@ mono_gc_card_table_nursery_check (void)
28672867
return !sgen_get_major_collector ()->is_concurrent;
28682868
}
28692869

2870-
/* Negative value to remove */
28712870
void
2872-
mono_gc_add_memory_pressure (gint64 value)
2871+
mono_gc_add_memory_pressure (guint64 value)
28732872
{
2874-
/* FIXME: Implement at some point? */
2873+
sgen_add_memory_pressure(value);
2874+
}
2875+
2876+
void
2877+
mono_gc_remove_memory_pressure (guint64 value)
2878+
{
2879+
sgen_remove_memory_pressure(value);
28752880
}
28762881

28772882
/*

src/mono/mono/sgen/sgen-gc.c

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4083,4 +4083,104 @@ sgen_check_canary_for_object (gpointer addr)
40834083
}
40844084
}
40854085

4086+
4087+
#define MEM_PRESSURE_COUNT 4
4088+
#define MAX_MEMORYPRESSURE_RATIO 10
4089+
guint64 memory_pressure_gc_count = 0;
4090+
guint64 memory_pressure_iteration = 0;
4091+
guint64 memory_pressure_adds[MEM_PRESSURE_COUNT] = {0, 0, 0, 0};
4092+
guint64 memory_pressure_removes[MEM_PRESSURE_COUNT] = {0, 0, 0, 0}; // history of memory pressure removals
4093+
const unsigned min_memorypressure_budget = 4 * 1024 * 1024; // 4 MB
4094+
4095+
// Resets pressure accounting after a gen2 GC has occurred.
4096+
static void check_pressure_counts ()
4097+
{
4098+
if (memory_pressure_gc_count != sgen_gc_collection_count(GENERATION_OLD)) {
4099+
memory_pressure_gc_count = sgen_gc_collection_count(GENERATION_OLD);
4100+
mono_atomic_inc_i64((gint64*)&memory_pressure_iteration);
4101+
4102+
guint32 p = memory_pressure_iteration % MEM_PRESSURE_COUNT;
4103+
4104+
memory_pressure_adds[p] = 0; // new pressure will be accumulated here
4105+
memory_pressure_removes[p] = 0;
4106+
}
4107+
}
4108+
4109+
void sgen_remove_memory_pressure (guint64 bytes_allocated)
4110+
{
4111+
check_pressure_counts();
4112+
4113+
guint32 p = memory_pressure_iteration % MEM_PRESSURE_COUNT;
4114+
4115+
mono_atomic_fetch_add_i64((gint64*)&memory_pressure_removes[p], bytes_allocated);
4116+
}
4117+
4118+
4119+
static gboolean pressure_check_heuristic (guint64 new_mem_value)
4120+
{
4121+
4122+
guint64 add = 0;
4123+
guint64 rem = 0;
4124+
4125+
guint p = memory_pressure_iteration % MEM_PRESSURE_COUNT;
4126+
4127+
for (gint i = 0; i < MEM_PRESSURE_COUNT; i++) {
4128+
add += memory_pressure_adds[i];
4129+
rem += memory_pressure_removes[i];
4130+
}
4131+
4132+
add -= memory_pressure_adds[p];
4133+
rem -= memory_pressure_removes[p];
4134+
4135+
if (new_mem_value >= min_memorypressure_budget) {
4136+
guint64 budget = min_memorypressure_budget;
4137+
4138+
if (memory_pressure_iteration >= MEM_PRESSURE_COUNT) { // wait until we have enough data points
4139+
// Adjust according to effectiveness of GC
4140+
// Scale budget according to past memory_pressure_adds / memory_pressure_removes ratio
4141+
if (add >= rem * MAX_MEMORYPRESSURE_RATIO)
4142+
{
4143+
budget = min_memorypressure_budget * MAX_MEMORYPRESSURE_RATIO;
4144+
}
4145+
else if (add > rem)
4146+
{
4147+
g_assert (rem != 0);
4148+
// Avoid overflow by calculating addPressure / remPressure as fixed point (1 = 1024)
4149+
budget = (add * 1024 / rem) * budget / 1024;
4150+
}
4151+
}
4152+
4153+
// If still over budget, check current managed heap size
4154+
if (new_mem_value >= budget) {
4155+
guint64 heap_over_3 = sgen_gc_info.heap_size_bytes / 3;
4156+
4157+
if (budget < heap_over_3) {
4158+
budget = heap_over_3;
4159+
}
4160+
if (new_mem_value >= budget) {
4161+
// last check - if we would exceed 20% of GC "duty cycle", do not trigger GC at this time
4162+
if ((size_t)(mono_time_since_last_stw() + time_last) > (time_last * 5)) {
4163+
return TRUE;
4164+
}
4165+
}
4166+
}
4167+
}
4168+
4169+
return FALSE;
4170+
}
4171+
4172+
void sgen_add_memory_pressure (guint64 bytes_allocated)
4173+
{
4174+
4175+
check_pressure_counts ();
4176+
4177+
guint p = memory_pressure_iteration % MEM_PRESSURE_COUNT;
4178+
4179+
guint64 new_mem_value = mono_atomic_fetch_add_i64 ((gint64*)&memory_pressure_adds[p], bytes_allocated);
4180+
4181+
if (pressure_check_heuristic(new_mem_value)) {
4182+
sgen_gc_collect (GENERATION_OLD);
4183+
check_pressure_counts ();
4184+
}
4185+
}
40864186
#endif /* HAVE_SGEN_GC */

src/mono/mono/sgen/sgen-gc.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1230,6 +1230,9 @@ sgen_dummy_use (gpointer v)
12301230
#endif
12311231
}
12321232

1233+
void sgen_add_memory_pressure(guint64 value);
1234+
void sgen_remove_memory_pressure(guint64 value);
1235+
12331236
#endif /* HAVE_SGEN_GC */
12341237

12351238
#endif /* __MONO_SGENGC_H__ */

0 commit comments

Comments
 (0)