_gchandle

This commit is contained in:
2023-08-27 11:13:27 +03:00
parent a8ef6e69fe
commit 70edfbb146
2 changed files with 99 additions and 33 deletions

View File

@@ -497,6 +497,8 @@ namespace FlaxEditor
Debug.LogException(ex); Debug.LogException(ex);
} }
ManagedHandle.ManagedHandlePool.TryCollectWeakHandles();
Profiler.EndEvent(); Profiler.EndEvent();
} }

View File

@@ -417,25 +417,26 @@ namespace FlaxEngine.Interop
private const int WeakPoolCollectionTimeThreshold = 500; private const int WeakPoolCollectionTimeThreshold = 500;
// Rolling numbers for handles, two bits reserved for the type // Rolling numbers for handles, two bits reserved for the type
private static ulong normalHandleAccumulator = ((ulong)GCHandleType.Normal << 62) & 0xC000000000000000; //private static ulong normalHandleAccumulator = ((ulong)GCHandleType.Normal << 62) & 0xC000000000000000;
private static ulong pinnedHandleAccumulator = ((ulong)GCHandleType.Pinned << 62) & 0xC000000000000000; //private static ulong pinnedHandleAccumulator = ((ulong)GCHandleType.Pinned << 62) & 0xC000000000000000;
private static ulong weakHandleAccumulator = ((ulong)GCHandleType.Weak << 62) & 0xC000000000000000; //private static ulong weakHandleAccumulator = ((ulong)GCHandleType.Weak << 62) & 0xC000000000000000;
// Dictionaries for storing the valid handles. // Dictionaries for storing the valid handles.
// Note: Using locks seems to be generally the fastest when adding or fetching from the dictionary. // Note: Using locks seems to be generally the fastest when adding or fetching from the dictionary.
// Concurrent dictionaries could also be considered, but they perform much slower when adding to the dictionary. // Concurrent dictionaries could also be considered, but they perform much slower when adding to the dictionary.
private static Dictionary<IntPtr, object> persistentPool = new(); //private static Dictionary<IntPtr, GCHandle> persistentPool = new();
private static Dictionary<IntPtr, GCHandle> pinnedPool = new(); //private static Dictionary<IntPtr, GCHandle> pinnedPool = new();
// TODO: Performance of pinned handles are poor at the moment due to GCHandle wrapping. // TODO: Performance of pinned handles are poor at the moment due to GCHandle wrapping.
// TODO: .NET8: Experiment with pinned arrays for faster pinning: https://github.com/dotnet/runtime/pull/89293 // TODO: .NET8: Experiment with pinned arrays for faster pinning: https://github.com/dotnet/runtime/pull/89293
// Manage double-buffered pool for weak handles in order to avoid collecting in-flight handles. // Manage double-buffered pool for weak handles in order to avoid collecting in-flight handles.
// Periodically when the pools are being accessed and conditions are met, the other pool is cleared and swapped. // Periodically when the pools are being accessed and conditions are met, the other pool is cleared and swapped.
private static Dictionary<IntPtr, object> weakPool = new(); private static HashSet<IntPtr> weakPool = new();
private static Dictionary<IntPtr, object> weakPoolOther = new(); private static HashSet<IntPtr> weakPoolOther = new();
private static object weakPoolLock = new object(); private static object weakPoolLock = new object();
private static ulong nextWeakPoolCollection; private static ulong nextWeakPoolCollection;
private static ulong weakPoolCycle;
private static int nextWeakPoolGCCollection; private static int nextWeakPoolGCCollection;
private static long lastWeakPoolCollectionTime; private static long lastWeakPoolCollectionTime;
@@ -444,28 +445,47 @@ namespace FlaxEngine.Interop
/// </summary> /// </summary>
internal static void TryCollectWeakHandles() internal static void TryCollectWeakHandles()
{ {
if (weakHandleAccumulator < nextWeakPoolCollection) nextWeakPoolCollection++;
if (nextWeakPoolCollection < 60)
return; return;
nextWeakPoolCollection = weakHandleAccumulator + 1000; nextWeakPoolCollection = 0;
// Try to swap pools after garbage collection or whenever the pool gets too large //ulong currentCycle = weakPoolCycle;
var gc0CollectionCount = GC.CollectionCount(0);
if (gc0CollectionCount < nextWeakPoolGCCollection && weakPool.Count < WeakPoolCollectionSizeThreshold)
return;
nextWeakPoolGCCollection = gc0CollectionCount + 1;
// Prevent huge allocations from swapping the pools in the middle of the operation //lock (weakPoolLock)
if (System.Diagnostics.Stopwatch.GetElapsedTime(lastWeakPoolCollectionTime).TotalMilliseconds < WeakPoolCollectionTimeThreshold) {
return; //if (currentCycle != weakPoolCycle)
lastWeakPoolCollectionTime = System.Diagnostics.Stopwatch.GetTimestamp(); // return;
// Swap the pools and release the oldest pool for GC weakPoolCycle++;
(weakPool, weakPoolOther) = (weakPoolOther, weakPool);
weakPool.Clear(); // Try to swap pools after garbage collection or whenever the pool gets too large
var gc0CollectionCount = GC.CollectionCount(0);
if (gc0CollectionCount < nextWeakPoolGCCollection && weakPool.Count < WeakPoolCollectionSizeThreshold)
return;
nextWeakPoolGCCollection = gc0CollectionCount + 1;
// Prevent huge allocations from swapping the pools in the middle of the operation
if (System.Diagnostics.Stopwatch.GetElapsedTime(lastWeakPoolCollectionTime).TotalMilliseconds < WeakPoolCollectionTimeThreshold)
return;
lastWeakPoolCollectionTime = System.Diagnostics.Stopwatch.GetTimestamp();
foreach (IntPtr ptr in weakPoolOther)
{
GCHandle gcHandle = GCHandle.FromIntPtr(ptr);
gcHandle.Free();
}
// Swap the pools and release the oldest pool for GC
(weakPool, weakPoolOther) = (weakPoolOther, weakPool);
weakPool.Clear();
//FlaxEditor.Editor.LogWarning("collected handles");
}
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] /*[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static IntPtr NewHandle(GCHandleType type) => type switch private static IntPtr NewHandle(GCHandleType type) => type switch
{ {
GCHandleType.Normal => (IntPtr)Interlocked.Increment(ref normalHandleAccumulator), GCHandleType.Normal => (IntPtr)Interlocked.Increment(ref normalHandleAccumulator),
@@ -473,14 +493,30 @@ namespace FlaxEngine.Interop
GCHandleType.Weak => (IntPtr)Interlocked.Increment(ref weakHandleAccumulator), GCHandleType.Weak => (IntPtr)Interlocked.Increment(ref weakHandleAccumulator),
GCHandleType.WeakTrackResurrection => (IntPtr)Interlocked.Increment(ref weakHandleAccumulator), GCHandleType.WeakTrackResurrection => (IntPtr)Interlocked.Increment(ref weakHandleAccumulator),
_ => throw new NotImplementedException(type.ToString()) _ => throw new NotImplementedException(type.ToString())
}; };*/
[MethodImpl(MethodImplOptions.AggressiveInlining)] //[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static GCHandleType GetHandleType(IntPtr handle) => (GCHandleType)(((ulong)handle & 0xC000000000000000) >> 62); //private static GCHandleType GetHandleType(IntPtr handle) => (GCHandleType)(((ulong)handle & 0xC000000000000000) >> 62);
internal static IntPtr AllocateHandle(object value, GCHandleType type) internal static IntPtr AllocateHandle(object value, GCHandleType type)
{ {
IntPtr handle = NewHandle(type); GCHandleType type2 = type;
if (type == GCHandleType.Weak || type == GCHandleType.WeakTrackResurrection)
{
type2 = GCHandleType.Normal;
}
GCHandle gcHandle = GCHandle.Alloc(value, type2);
IntPtr handle = GCHandle.ToIntPtr(gcHandle);
if (type == GCHandleType.Weak || type == GCHandleType.WeakTrackResurrection)
{
//TryCollectWeakHandles();
weakPool.Add(handle/*, gcHandle.Target*/);
}
return handle;
/*IntPtr handle = NewHandle(type);
switch (type) switch (type)
{ {
case GCHandleType.Normal: case GCHandleType.Normal:
@@ -500,12 +536,22 @@ namespace FlaxEngine.Interop
} }
break; break;
} }
return handle; return handle;*/
} }
internal static object GetObject(IntPtr handle) internal static object GetObject(IntPtr handle)
{ {
switch (GetHandleType(handle)) //if (weakPool.Contains(handle) || weakPoolOther.Contains(handle))
// handle = handle;
GCHandle gcHandle = GCHandle.FromIntPtr(handle);
if (!gcHandle.IsAllocated)
throw new NativeInteropException("Invalid ManagedHandle");
return gcHandle.Target;
/*switch (GetHandleType(handle))
{ {
case GCHandleType.Normal: case GCHandleType.Normal:
lock (persistentPool) lock (persistentPool)
@@ -533,12 +579,19 @@ namespace FlaxEngine.Interop
} }
break; break;
} }
throw new NativeInteropException("Invalid ManagedHandle"); throw new NativeInteropException("Invalid ManagedHandle");*/
} }
internal static void SetObject(IntPtr handle, object value) internal static void SetObject(IntPtr handle, object value)
{ {
switch (GetHandleType(handle)) GCHandle gcHandle = GCHandle.FromIntPtr(handle);
if (!gcHandle.IsAllocated)
throw new NativeInteropException("Invalid ManagedHandle");
gcHandle.Target = value;
/*switch (GetHandleType(handle))
{ {
case GCHandleType.Normal: case GCHandleType.Normal:
lock (persistentPool) lock (persistentPool)
@@ -586,12 +639,23 @@ namespace FlaxEngine.Interop
} }
break; break;
} }
throw new NativeInteropException("Invalid ManagedHandle"); throw new NativeInteropException("Invalid ManagedHandle");*/
} }
internal static void FreeHandle(IntPtr handle) internal static void FreeHandle(IntPtr handle)
{ {
switch (GetHandleType(handle)) if (weakPool.Contains(handle) || weakPoolOther.Contains(handle))
return;
GCHandle gcHandle = GCHandle.FromIntPtr(handle);
if (!gcHandle.IsAllocated)
throw new NativeInteropException("Invalid ManagedHandle");
gcHandle.Free();
//if (weakPool.Contains(handle) || )
/*switch (GetHandleType(handle))
{ {
case GCHandleType.Normal: case GCHandleType.Normal:
lock (persistentPool) lock (persistentPool)
@@ -616,7 +680,7 @@ namespace FlaxEngine.Interop
TryCollectWeakHandles(); TryCollectWeakHandles();
return; return;
} }
throw new NativeInteropException("Invalid ManagedHandle"); throw new NativeInteropException("Invalid ManagedHandle");*/
} }
} }
} }