From 2a28a2466ed7da1e5857959110d77eee89249a7b Mon Sep 17 00:00:00 2001 From: = Date: Mon, 13 Jan 2020 22:01:55 -0600 Subject: [PATCH 1/7] [#51] Fix performance issue with Object Store When BaseMaxSimultaneous was set to a large number the object-store was spending to much time finding the handle. This was fixed using a dictionary where the keys have the objects full hash. --- Unity/Assets/NativeScript/Bindings.cs | 85 +++++++-------------------- 1 file changed, 20 insertions(+), 65 deletions(-) diff --git a/Unity/Assets/NativeScript/Bindings.cs b/Unity/Assets/NativeScript/Bindings.cs index 07fbb24..4e694ee 100644 --- a/Unity/Assets/NativeScript/Bindings.cs +++ b/Unity/Assets/NativeScript/Bindings.cs @@ -4,6 +4,7 @@ using System.Collections; using System.IO; using System.Runtime.InteropServices; +using System.Collections.Generic; using UnityEngine; @@ -24,19 +25,13 @@ public static class Bindings // Holds objects and provides handles to them in the form of ints public static class ObjectStore { + static Dictionary handleLookupByHash; + static Dictionary hashLookupByHandle; + static Stack freeHandleStack; + // Stored objects. The first is never used so 0 can be "null". static object[] objects; - // Stack of available handles - static int[] handles; - - // Hash table of stored objects to their handles. - static object[] keys; - static int[] values; - - // Index of the next available handle - static int nextHandleIndex; - // The maximum number of objects to store. Must be positive. static int maxObjects; @@ -49,19 +44,13 @@ public static void Init(int maxObjects) objects = new object[maxObjects + 1]; // Initialize the handles stack as 1, 2, 3, ... - handles = new int[maxObjects]; for ( int i = 0, handle = maxObjects; i < maxObjects; ++i, --handle) { - handles[i] = handle; + freeHandleStack.Push(handle); } - nextHandleIndex = maxObjects - 1; - - // Initialize the hash table - keys = new object[maxObjects]; - values = new int[maxObjects]; } public static int Store(object obj) @@ -75,27 +64,15 @@ public static int Store(object obj) lock (objects) { // Pop a handle off the stack - int handle = handles[nextHandleIndex]; - nextHandleIndex--; + int handle = freeHandleStack.Pop(); // Store the object objects[handle] = obj; // Insert into the hash table - int initialIndex = (int)( - ((uint)obj.GetHashCode()) % maxObjects); - int index = initialIndex; - do - { - if (object.ReferenceEquals(keys[index], null)) - { - keys[index] = obj; - values[index] = handle; - break; - } - index = (index + 1) % maxObjects; - } - while (index != initialIndex); + uint hash = (uint)obj.GetHashCode(); + handleLookupByHash.Add(hash, handle); + hashLookupByHandle.Add(handle, hash); return handle; } @@ -108,6 +85,7 @@ public static object Get(int handle) public static int GetHandle(object obj) { + // Null is always zero if (object.ReferenceEquals(obj, null)) { @@ -116,19 +94,12 @@ public static int GetHandle(object obj) lock (objects) { - // Look up the object in the hash table - int initialIndex = (int)( - ((uint)obj.GetHashCode()) % maxObjects); - int index = initialIndex; - do + // Look up the handle in the hash table + uint hash = (uint)obj.GetHashCode(); + if (handleLookupByHash.ContainsKey(hash)) { - if (object.ReferenceEquals(keys[index], obj)) - { - return values[index]; - } - index = (index + 1) % maxObjects; + return handleLookupByHash[hash]; } - while (index != initialIndex); } // Object not found @@ -150,28 +121,12 @@ public static object Remove(int handle) objects[handle] = null; // Push the handle onto the stack - nextHandleIndex++; - handles[nextHandleIndex] = handle; + freeHandleStack.Push(handle); - // Remove the object from the hash table - int initialIndex = (int)( - ((uint)obj.GetHashCode()) % maxObjects); - int index = initialIndex; - do - { - if (object.ReferenceEquals(keys[index], obj)) - { - // Only the key needs to be removed (set to null) - // because values corresponding to null will never - // be read and the values are just integers, so - // we're not holding on to a managed reference that - // will prevent GC. - keys[index] = null; - break; - } - index = (index + 1) % maxObjects; - } - while (index != initialIndex); + // Remove the object from the hash dictionary's + var hash = hashLookupByHandle[handle]; + handleLookupByHash.Remove(hash); + hashLookupByHandle.Remove(handle); return obj; } From 42116abf8aac16be2df63c20f56aa0fb19a11b02 Mon Sep 17 00:00:00 2001 From: = Date: Tue, 14 Jan 2020 00:50:14 -0600 Subject: [PATCH 2/7] [#51] add cache construction in the init method --- Unity/Assets/NativeScript/Bindings.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Unity/Assets/NativeScript/Bindings.cs b/Unity/Assets/NativeScript/Bindings.cs index 4e694ee..8ea312b 100644 --- a/Unity/Assets/NativeScript/Bindings.cs +++ b/Unity/Assets/NativeScript/Bindings.cs @@ -38,6 +38,9 @@ public static class ObjectStore public static void Init(int maxObjects) { ObjectStore.maxObjects = maxObjects; + handleLookupByHash = new Dictionary (); + hashLookupByHandle = new Dictionary (); + freeHandleStack = new Stack (); // Initialize the objects as all null plus room for the // first to always be null. From afd3c8e2bdc992c4639eb42a475bf70aa95a6724 Mon Sep 17 00:00:00 2001 From: = Date: Tue, 14 Jan 2020 23:48:48 -0600 Subject: [PATCH 3/7] [#51] Add collision handling for object store --- Unity/Assets/NativeScript/Bindings.cs | 73 ++++++++++++++++++++------- 1 file changed, 54 insertions(+), 19 deletions(-) diff --git a/Unity/Assets/NativeScript/Bindings.cs b/Unity/Assets/NativeScript/Bindings.cs index 8ea312b..3310650 100644 --- a/Unity/Assets/NativeScript/Bindings.cs +++ b/Unity/Assets/NativeScript/Bindings.cs @@ -25,9 +25,10 @@ public static class Bindings // Holds objects and provides handles to them in the form of ints public static class ObjectStore { - static Dictionary handleLookupByHash; + static Dictionary> handleBucketByHash; static Dictionary hashLookupByHandle; - static Stack freeHandleStack; + static HashSet hash; + static Stack handles; // Stored objects. The first is never used so 0 can be "null". static object[] objects; @@ -38,9 +39,9 @@ public static class ObjectStore public static void Init(int maxObjects) { ObjectStore.maxObjects = maxObjects; - handleLookupByHash = new Dictionary (); + handleBucketByHash = new Dictionary> (); hashLookupByHandle = new Dictionary (); - freeHandleStack = new Stack (); + handles = new Stack (); // Initialize the objects as all null plus room for the // first to always be null. @@ -52,7 +53,7 @@ public static void Init(int maxObjects) i < maxObjects; ++i, --handle) { - freeHandleStack.Push(handle); + handles.Push(handle); } } @@ -66,16 +67,28 @@ public static int Store(object obj) lock (objects) { + // Get the hash of the object + uint hash = (uint)obj.GetHashCode(); + // Pop a handle off the stack - int handle = freeHandleStack.Pop(); + int handle = handles.Pop(); // Store the object objects[handle] = obj; - - // Insert into the hash table - uint hash = (uint)obj.GetHashCode(); - handleLookupByHash.Add(hash, handle); - hashLookupByHandle.Add(handle, hash); + + List handleBucket = null; + + // Create new handle bucket if it does not exist + if (!handleBucketByHash.TryGetValue(hash, out handleBucket)) + { + handleBucket = new List (); + handleBucketByHash[hash] = handleBucket; + } + + // Insert into hash table + handleBucket.Add(handle); + + hashLookupByHandle[handle] = hash; return handle; } @@ -88,7 +101,6 @@ public static object Get(int handle) public static int GetHandle(object obj) { - // Null is always zero if (object.ReferenceEquals(obj, null)) { @@ -99,9 +111,20 @@ public static int GetHandle(object obj) { // Look up the handle in the hash table uint hash = (uint)obj.GetHashCode(); - if (handleLookupByHash.ContainsKey(hash)) + List handleBucket = null; + + if (handleBucketByHash.TryGetValue(hash, out handleBucket)) { - return handleLookupByHash[hash]; + for (int i = 0; i < handleBucket.Count; i++) + { + int handleInBucket = handleBucket[i]; + object objectInBucket = objects[handleInBucket]; + + if (object.ReferenceEquals(objectInBucket, obj)) + { + return handleInBucket; + } + } } } @@ -124,12 +147,24 @@ public static object Remove(int handle) objects[handle] = null; // Push the handle onto the stack - freeHandleStack.Push(handle); - + handles.Push(handle); + + uint hash = hashLookupByHandle[handle]; + List handleBucket = null; + // Remove the object from the hash dictionary's - var hash = hashLookupByHandle[handle]; - handleLookupByHash.Remove(hash); - hashLookupByHandle.Remove(handle); + if (handleBucketByHash.TryGetValue(hash, out handleBucket)) + { + for (int i = 0; i < handleBucket.Count; i++) + { + int handleInBucket = handleBucket[i]; + if (handleInBucket == handle) + { + handleBucket.RemoveAt(i); + break; + } + } + } return obj; } From 3b519ac840c0a8eed9ca6acbc307d4cadb63ad08 Mon Sep 17 00:00:00 2001 From: = Date: Wed, 15 Jan 2020 19:32:32 -0600 Subject: [PATCH 4/7] [#51] change object-store to use a dictionary to cache objects Dictionary will take care of hashing/collisions for us. --- Unity/Assets/NativeScript/Bindings.cs | 67 ++++++--------------------- 1 file changed, 14 insertions(+), 53 deletions(-) diff --git a/Unity/Assets/NativeScript/Bindings.cs b/Unity/Assets/NativeScript/Bindings.cs index 3310650..3f83a8e 100644 --- a/Unity/Assets/NativeScript/Bindings.cs +++ b/Unity/Assets/NativeScript/Bindings.cs @@ -25,9 +25,10 @@ public static class Bindings // Holds objects and provides handles to them in the form of ints public static class ObjectStore { - static Dictionary> handleBucketByHash; - static Dictionary hashLookupByHandle; - static HashSet hash; + // Lookup handles by object. + static Dictionary objectHandleCache; + + // Stack of available handles. static Stack handles; // Stored objects. The first is never used so 0 can be "null". @@ -39,9 +40,8 @@ public static class ObjectStore public static void Init(int maxObjects) { ObjectStore.maxObjects = maxObjects; - handleBucketByHash = new Dictionary> (); - hashLookupByHandle = new Dictionary (); - handles = new Stack (); + objectHandleCache = new Dictionary (maxObjects); + handles = new Stack (maxObjects); // Initialize the objects as all null plus room for the // first to always be null. @@ -67,28 +67,12 @@ public static int Store(object obj) lock (objects) { - // Get the hash of the object - uint hash = (uint)obj.GetHashCode(); - // Pop a handle off the stack int handle = handles.Pop(); // Store the object objects[handle] = obj; - - List handleBucket = null; - - // Create new handle bucket if it does not exist - if (!handleBucketByHash.TryGetValue(hash, out handleBucket)) - { - handleBucket = new List (); - handleBucketByHash[hash] = handleBucket; - } - - // Insert into hash table - handleBucket.Add(handle); - - hashLookupByHandle[handle] = hash; + objectHandleCache.Add(obj, handle); return handle; } @@ -109,22 +93,13 @@ public static int GetHandle(object obj) lock (objects) { - // Look up the handle in the hash table - uint hash = (uint)obj.GetHashCode(); - List handleBucket = null; + // A handle with a value of 0 is NULL + int handle = 0; - if (handleBucketByHash.TryGetValue(hash, out handleBucket)) + // Get handle from object cache + if (objectHandleCache.TryGetValue(obj, out handle)) { - for (int i = 0; i < handleBucket.Count; i++) - { - int handleInBucket = handleBucket[i]; - object objectInBucket = objects[handleInBucket]; - - if (object.ReferenceEquals(objectInBucket, obj)) - { - return handleInBucket; - } - } + return handle; } } @@ -149,22 +124,8 @@ public static object Remove(int handle) // Push the handle onto the stack handles.Push(handle); - uint hash = hashLookupByHandle[handle]; - List handleBucket = null; - - // Remove the object from the hash dictionary's - if (handleBucketByHash.TryGetValue(hash, out handleBucket)) - { - for (int i = 0; i < handleBucket.Count; i++) - { - int handleInBucket = handleBucket[i]; - if (handleInBucket == handle) - { - handleBucket.RemoveAt(i); - break; - } - } - } + // Remove the object from the cache + objectHandleCache.Remove(obj); return obj; } From bfaf5ee167308ce330c6a72b7a3614f31666a7b6 Mon Sep 17 00:00:00 2001 From: = Date: Sat, 18 Jan 2020 16:05:23 -0600 Subject: [PATCH 5/7] [#51] fix formatting and remove unnecessary variable initialization --- Unity/Assets/NativeScript/Bindings.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Unity/Assets/NativeScript/Bindings.cs b/Unity/Assets/NativeScript/Bindings.cs index 3f83a8e..72a594a 100644 --- a/Unity/Assets/NativeScript/Bindings.cs +++ b/Unity/Assets/NativeScript/Bindings.cs @@ -40,7 +40,7 @@ public static class ObjectStore public static void Init(int maxObjects) { ObjectStore.maxObjects = maxObjects; - objectHandleCache = new Dictionary (maxObjects); + objectHandleCache = new Dictionary(maxObjects); handles = new Stack (maxObjects); // Initialize the objects as all null plus room for the @@ -93,8 +93,7 @@ public static int GetHandle(object obj) lock (objects) { - // A handle with a value of 0 is NULL - int handle = 0; + int handle; // Get handle from object cache if (objectHandleCache.TryGetValue(obj, out handle)) From 0a6bdb00f5d6cd5d42c9fcfbce758bc5fcdcab6d Mon Sep 17 00:00:00 2001 From: = Date: Sat, 18 Jan 2020 16:26:34 -0600 Subject: [PATCH 6/7] [#51] Revert ObjectStore handles code to original implementation --- Unity/Assets/NativeScript/Bindings.cs | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/Unity/Assets/NativeScript/Bindings.cs b/Unity/Assets/NativeScript/Bindings.cs index 72a594a..04aef29 100644 --- a/Unity/Assets/NativeScript/Bindings.cs +++ b/Unity/Assets/NativeScript/Bindings.cs @@ -28,11 +28,14 @@ public static class ObjectStore // Lookup handles by object. static Dictionary objectHandleCache; - // Stack of available handles. - static Stack handles; - // Stored objects. The first is never used so 0 can be "null". static object[] objects; + + // Stack of available handles. + static int[] handles; + + // Index of the next available handle + static int nextHandleIndex; // The maximum number of objects to store. Must be positive. static int maxObjects; @@ -41,20 +44,21 @@ public static void Init(int maxObjects) { ObjectStore.maxObjects = maxObjects; objectHandleCache = new Dictionary(maxObjects); - handles = new Stack (maxObjects); // Initialize the objects as all null plus room for the // first to always be null. objects = new object[maxObjects + 1]; // Initialize the handles stack as 1, 2, 3, ... + handles = new int[maxObjects]; for ( int i = 0, handle = maxObjects; i < maxObjects; ++i, --handle) { - handles.Push(handle); + handles[i] = handle; } + nextHandleIndex = maxObjects - 1; } public static int Store(object obj) @@ -68,7 +72,8 @@ public static int Store(object obj) lock (objects) { // Pop a handle off the stack - int handle = handles.Pop(); + int handle = handles[nextHandleIndex]; + nextHandleIndex--; // Store the object objects[handle] = obj; @@ -121,7 +126,8 @@ public static object Remove(int handle) objects[handle] = null; // Push the handle onto the stack - handles.Push(handle); + nextHandleIndex++; + handles[nextHandleIndex] = handle; // Remove the object from the cache objectHandleCache.Remove(obj); From 1c0b627801613a5ffc9c34d653ce5e9c43bfe802 Mon Sep 17 00:00:00 2001 From: = Date: Sat, 18 Jan 2020 16:28:55 -0600 Subject: [PATCH 7/7] [#51] fix minor formatting error --- Unity/Assets/NativeScript/Bindings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Unity/Assets/NativeScript/Bindings.cs b/Unity/Assets/NativeScript/Bindings.cs index 04aef29..72ac143 100644 --- a/Unity/Assets/NativeScript/Bindings.cs +++ b/Unity/Assets/NativeScript/Bindings.cs @@ -36,7 +36,7 @@ public static class ObjectStore // Index of the next available handle static int nextHandleIndex; - + // The maximum number of objects to store. Must be positive. static int maxObjects;