Ejemplo n.º 1
0
        public void Test_TRexSpatialMemoryCacheTests_EvictedItemsRemoval()
        {
            const int _originX = 123;
            const int _originY = 456;
            const int _size    = 1000;

            // Create the cache with enough elements to hold one per context without eviction
            using (var cache = new TRexSpatialMemoryCache(1, 1000000, 0.5))
            {
                // Make the number of contexts requested, and a separate item to be placed in each one
                var context = cache.LocateOrCreateContext(Guid.Empty, GridDataType.Height, "fingerprint");

                var item1 = new TRexSpatialMemoryCacheContextTests_Element
                {
                    SizeInBytes  = _size,
                    CacheOriginX = _originX,
                    CacheOriginY = _originY
                };

                var item2 = new TRexSpatialMemoryCacheContextTests_Element
                {
                    SizeInBytes  = _size,
                    CacheOriginX = _originX + SubGridTreeConsts.SubGridTreeDimension,
                    CacheOriginY = _originY + SubGridTreeConsts.SubGridTreeDimension
                };

                cache.Add(context, item1, context.InvalidationVersion).Should().Be(CacheContextAdditionResult.Added);
                cache.Add(context, item2, context.InvalidationVersion).Should().Be(CacheContextAdditionResult.Added);

                Assert.True(context.TokenCount == 1, "Token count not one after addition of second element forcing eviction of first");
                Assert.True(cache.Get(context, item1.CacheOriginX, item1.CacheOriginY) == null, "Able to request item1 after it should have been evicted for item2");
                Assert.True(cache.Get(context, item2.CacheOriginX, item2.CacheOriginY) != null, "Unable to request item2 after it should have replaced item1");
            }
        }
Ejemplo n.º 2
0
        public void Test_TRexSpatialMemoryCacheTests_ElementTimeBasedExpiry()
        {
            using (var cache = new TRexSpatialMemoryCache(10, 1000000, 0.5))
            {
                // Create a context with an expiry time of one second
                var context = cache.LocateOrCreateContext(Guid.Empty, GridDataType.All, "fingerprint", new TimeSpan(0, 0, 0, 0, 500));

                // Add an item, verify it is there, wait for a seconds then attempt to get the
                // item. Verify the result is null and that the item is no longer present in the cache

                cache.Add(context, new TRexSpatialMemoryCacheContextTests_Element
                {
                    CacheOriginX = 1000,
                    CacheOriginY = 1000,
                    SizeInBytes  = 1000
                }, context.InvalidationVersion);

                Assert.True(context.TokenCount == 1, "Failed to add new item to context");

                System.Threading.Thread.Sleep(1000); // Allow the item to expire

                Assert.Null(cache.Get(context, 1000, 1000));
                Assert.True(context.TokenCount == 0, "Element not retired on Get() after expiry date");
            }
        }
Ejemplo n.º 3
0
        public void Test_TRexSpatialMemoryCacheTests_AddAndRetrieveItem_OneContext()
        {
            const int _originX = 123;
            const int _originY = 456;
            const int _size    = 1000;

            using (var cache = new TRexSpatialMemoryCache(100, 1000000, 0.5))
            {
                var context = cache.LocateOrCreateContext(Guid.Empty, GridDataType.Height, "fingerprint");

                cache.Add(context, new TRexSpatialMemoryCacheContextTests_Element
                {
                    SizeInBytes  = _size,
                    CacheOriginX = _originX,
                    CacheOriginY = _originY,
                }, context.InvalidationVersion).Should().Be(CacheContextAdditionResult.Added);

                Assert.True(context.TokenCount == 1, "Context token count not one after adding single item");

                var gotItem = cache.Get(context, _originX, _originY);

                Assert.True(gotItem != null, "Failed to retrieve added entry");

                Assert.Equal(_originX, gotItem.CacheOriginX);
                Assert.Equal(_originY, gotItem.CacheOriginY);
                Assert.Equal(_size, gotItem.IndicativeSizeInBytes());
            }
        }
Ejemplo n.º 4
0
        public void Test_TRexSpatialMemoryCacheTests_AddAndRetrieveManyItems_OneContext()
        {
            const int _originX = 0;
            const int _originY = 0;
            const int _size    = 10;

            using (var cache = new TRexSpatialMemoryCache(100000, 1000000, 0.5))
            {
                var context = cache.LocateOrCreateContext(Guid.Empty, GridDataType.Height, "fingerprint");

                for (int i = 0; i < 100; i++)
                {
                    for (int j = 0; j < 100; j++)
                    {
                        Assert.True(cache.Add(context, new TRexSpatialMemoryCacheContextTests_Element()
                        {
                            SizeInBytes  = _size,
                            CacheOriginX = (int)(_originX + i * SubGridTreeConsts.SubGridTreeDimension),
                            CacheOriginY = (int)(_originY + j * SubGridTreeConsts.SubGridTreeDimension)
                        }, context.InvalidationVersion) == CacheContextAdditionResult.Added, $"Failed to add item to cache at {i}:{j}");
                    }
                }

                Assert.True(context.TokenCount == 10000, $"Context token count not 10000 after adding 10000 items, it is {context.TokenCount} instead");

                for (int i = 0; i < 100; i++)
                {
                    for (int j = 0; j < 100; j++)
                    {
                        int x = (int)(_originX + i * SubGridTreeConsts.SubGridTreeDimension);
                        int y = (int)(_originY + j * SubGridTreeConsts.SubGridTreeDimension);

                        var gotItem = cache.Get(context, x, y);
                        Assert.True(gotItem != null, "Failed to retrieve added entry");

                        Assert.Equal(x, gotItem.CacheOriginX);
                        Assert.Equal(y, gotItem.CacheOriginY);
                        Assert.Equal(_size, gotItem.IndicativeSizeInBytes());
                    }
                }
            }
        }
Ejemplo n.º 5
0
        public void Test_TRexSpatialMemoryCacheTests_Get()
        {
            using (var cache = new TRexSpatialMemoryCache(10, 1000000, 0.5))
            {
                // Create a context with an expiry time of one second
                var context = cache.LocateOrCreateContext(Guid.Empty, GridDataType.Height, "fingerprint");
                cache.Add(context, new TRexSpatialMemoryCacheContextTests_Element
                {
                    CacheOriginX = 1000,
                    CacheOriginY = 1000,
                    SizeInBytes  = 1000
                }, context.InvalidationVersion).Should().Be(CacheContextAdditionResult.Added);

                Assert.True(context.TokenCount == 1, "Failed to add new item to context");

                var item = cache.Get(context, 1000, 1000);

                Assert.NotNull(item);
                Assert.True(item.CacheOriginX == 1000 && item.CacheOriginY == 1000);
            }
        }
Ejemplo n.º 6
0
        public void Test_TRexSpatialMemoryCacheTests_AddAndRetrieveItem_ManyContexts(int numContexts)
        {
            const int _originX = 123;
            const int _originY = 456;
            const int _size    = 10;

            // Create the cache with enough elements to hold one per context without eviction
            using (var cache = new TRexSpatialMemoryCache(numContexts, 1000000, 0.5))
            {
                // Make the number ofg contexts requested, and a separate item to be placed in each one
                var contexts = Enumerable.Range(0, numContexts).Select(x => cache.LocateOrCreateContext(Guid.Empty, GridDataType.Height, $"fingerprint:{x}")).ToArray();

                var items = Enumerable.Range(0, numContexts).Select(x => new TRexSpatialMemoryCacheContextTests_Element
                {
                    SizeInBytes  = _size,
                    CacheOriginX = (int)(_originX + x * SubGridTreeConsts.SubGridTreeDimension),
                    CacheOriginY = (int)(_originY + x * SubGridTreeConsts.SubGridTreeDimension)
                }).ToArray();

                for (int i = 0; i < numContexts; i++)
                {
                    cache.Add(contexts[i], items[i], contexts[i].InvalidationVersion).Should().Be(CacheContextAdditionResult.Added);

                    Assert.True(contexts[i].TokenCount == 1, "Context token count not one after adding single item");
                }

                for (int i = 0; i < numContexts; i++)
                {
                    var gotItem = cache.Get(contexts[i], items[i].CacheOriginX, items[i].CacheOriginY);

                    Assert.True(gotItem != null, "Failed to retrieve added entry");
                    Assert.True(ReferenceEquals(items[i], gotItem), $"Got item not same as elements in items array at index {i}");
                    Assert.Equal(items[i].CacheOriginX, gotItem.CacheOriginX);
                    Assert.Equal(items[i].CacheOriginY, gotItem.CacheOriginY);
                    Assert.Equal(items[i].IndicativeSizeInBytes(), gotItem.IndicativeSizeInBytes());
                }
            }
        }
Ejemplo n.º 7
0
        public void CacheContext_Tenancy_IsUpheld_OnConcurrentRequests_WithLRUEvictionOnSmallCacheSize()
        {
            using var cache = new TRexSpatialMemoryCache(100, 1000000, 0.1);

            // Create a context with default invalidation sensitivity, add some data to it
            // and validate that a change bitmask causes appropriate invalidation in concurrent operations
            var contextHeight    = cache.LocateOrCreateContext(Guid.Empty, GridDataType.Height, "fingerprintHeight");
            var contextPassCount = cache.LocateOrCreateContext(Guid.Empty, GridDataType.PassCount, "fingerprintPasscount");

            var contexts = new[] { contextHeight, contextPassCount };

            var itemsHeight    = new TRexSpatialMemoryCacheContextTests_Element[100, 100];
            var itemsPassCount = new TRexSpatialMemoryCacheContextTests_Element[100, 100];

            var items = new[] { itemsHeight, itemsPassCount };

            // Create the bitmask
            ISubGridTreeBitMask mask = new SubGridTreeSubGridExistenceBitMask();

            for (var i = 0; i < 100; i++)
            {
                for (var j = 0; j < 100; j++)
                {
                    mask[(i * SubGridTreeConsts.SubGridTreeDimension) >> SubGridTreeConsts.SubGridIndexBitsPerLevel,
                         (j * SubGridTreeConsts.SubGridTreeDimension) >> SubGridTreeConsts.SubGridIndexBitsPerLevel] = true;

                    items.ForEach((x, index) => x[i, j] = new TRexSpatialMemoryCacheContextTests_Element
                    {
                        Context      = contexts[index],
                        CacheOriginX = i * SubGridTreeConsts.SubGridTreeDimension,
                        CacheOriginY = j * SubGridTreeConsts.SubGridTreeDimension, SizeInBytes = 1
                    });
                }
            }

//      var additionComplete = false;
//      var invalidationComplete = false;

            // Progressively add elements in the cache forcing elements to be removed to accomodate them given the cache's small size
            var additionTask = Task.Run(() => {
                for (var loopCount = 0; loopCount < 100; loopCount++)
                {
                    for (var i = 0; i < 100; i++)
                    {
                        for (var j = 0; j < 100; j++)
                        {
                            for (var contextIndex = 0; contextIndex < contexts.Length; contextIndex++)
                            {
                                TRexSpatialMemoryCacheContextTests_Element elem;
                                if ((elem = (TRexSpatialMemoryCacheContextTests_Element)cache.Get(contexts[contextIndex], i, j)) != null)
                                {
                                    elem.Context.Should().Be(contexts[contextIndex]);
                                }
                                else
                                {
                                    cache.Add(contexts[contextIndex], items[contextIndex][i, j], contexts[contextIndex].InvalidationVersion).Should().Be(CacheContextAdditionResult.Added);
                                }
                            }
                        }
                    }
                }

//        additionComplete = true;
            });

            /*
             * var invalidationTask = Task.Run(() =>
             * {
             * for (var loopCount = 0; loopCount < 100; loopCount++)
             * {
             *  for (var i = 99; i >= 0; i--)
             *  {
             *    for (var j = 99; j >= 0; j--)
             *    {
             *      foreach (var context in contexts)
             *      {
             *        // Empty contexts are ignored
             *        if (context.TokenCount > 0)
             *        {
             *          context.InvalidateSubGrid(i * SubGridTreeConsts.SubGridTreeDimension, j * SubGridTreeConsts.SubGridTreeDimension, out var subGridPresentForInvalidation);
             *        }
             *      }
             *    }
             *  }
             * }
             *
             * invalidationComplete = true;
             * });
             */

/*      var numDesignInvalidations = 0;
 *    var designUpdateTask = Task.Run(async () =>
 *    {
 *      while (!additionComplete || !invalidationComplete)
 *      {
 *        // Mimic design invalidation by periodically invalidating all sub grids
 *        await Task.Delay(10);
 *        contexts[numDesignInvalidations % contexts.Length].InvalidateAllSubGrids();
 *
 *        numDesignInvalidations++;
 *      }
 *    });
 *
 */
            Task.WaitAll(new[] { additionTask /*, invalidationTask, designUpdateTask*/ });

//      numDesignInvalidations.Should().BeGreaterThan(0);
        }
Ejemplo n.º 8
0
        public void CacheContext_Pressure_MRULRUManagement()
        {
            using var cache = new TRexSpatialMemoryCache(100, 1000000, 0.1);

            // Create a context with default invalidation sensitivity, add some data to it
            // and validate that a change bitmask causes appropriate invalidation in concurrent operations
            var contextHeight    = cache.LocateOrCreateContext(Guid.Empty, GridDataType.Height, "fingerprintHeight");
            var contextPassCount = cache.LocateOrCreateContext(Guid.Empty, GridDataType.PassCount, "fingerprintPasscount");

            var contexts = new[] { contextHeight, contextPassCount };

            var itemsHeight    = new TRexSpatialMemoryCacheContextTests_Element[100, 100];
            var itemsPassCount = new TRexSpatialMemoryCacheContextTests_Element[100, 100];

            var items = new[] { itemsHeight, itemsPassCount };

            for (var i = 0; i < 100; i++)
            {
                for (var j = 0; j < 100; j++)
                {
                    items.ForEach((x, index) => x[i, j] = new TRexSpatialMemoryCacheContextTests_Element
                    {
                        Context      = contexts[index],
                        CacheOriginX = i * SubGridTreeConsts.SubGridTreeDimension,
                        CacheOriginY = j * SubGridTreeConsts.SubGridTreeDimension,
                        SizeInBytes  = 1
                    });
                }
            }

            // Progressively add elements in the cache forcing elements to be removed to accomodate them given the cache's small size
            // Check the retrieved element is also in the expected cache
            var additionTask = Task.Run(() => {
                for (var loopCount = 0; loopCount < 100; loopCount++)
                {
                    for (var i = 0; i < 100; i++)
                    {
                        for (var j = 0; j < 100; j++)
                        {
                            for (var contextIndex = 0; contextIndex < contexts.Length; contextIndex++)
                            {
                                TRexSpatialMemoryCacheContextTests_Element elem;
                                if ((elem = (TRexSpatialMemoryCacheContextTests_Element)cache.Get(contexts[contextIndex], i, j)) != null)
                                {
                                    elem.Context.Should().Be(contexts[contextIndex]);
                                }
                                else
                                {
                                    cache.Add(contexts[contextIndex], items[contextIndex][i, j], contexts[contextIndex].InvalidationVersion).Should().Be(CacheContextAdditionResult.Added);
                                }
                            }
                        }
                    }
                }
            });

            Task.WaitAll(new[] { additionTask });
        }
Ejemplo n.º 9
0
        public void Test_TRexSpatialMemoryCacheTests_ConcurrentAccessWithDesignAndProductionDataIngestInvalidation()
        {
            using var cache = new TRexSpatialMemoryCache(20000, 1000000, 0.5);

            // Create a context with default invalidation sensitivity, add some data to it
            // and validate that a change bitmask causes appropriate invalidation in concurrent operations
            var contextHeight    = cache.LocateOrCreateContext(Guid.Empty, GridDataType.Height, "fingerprintHeight");
            var contextPassCount = cache.LocateOrCreateContext(Guid.Empty, GridDataType.PassCount, "fingerprintPasscount");

            var contexts = new[] { contextHeight, contextPassCount };

            var itemsHeight    = new TRexSpatialMemoryCacheContextTests_Element[100, 100];
            var itemsPassCount = new TRexSpatialMemoryCacheContextTests_Element[100, 100];

            var items = new[] { itemsHeight, itemsPassCount };

            // Create the bitmask
            ISubGridTreeBitMask mask = new SubGridTreeSubGridExistenceBitMask();

            for (var i = 0; i < 100; i++)
            {
                for (var j = 0; j < 100; j++)
                {
                    mask[(i * SubGridTreeConsts.SubGridTreeDimension) >> SubGridTreeConsts.SubGridIndexBitsPerLevel,
                         (j * SubGridTreeConsts.SubGridTreeDimension) >> SubGridTreeConsts.SubGridIndexBitsPerLevel] = true;

                    items.ForEach(x => x[i, j] = new TRexSpatialMemoryCacheContextTests_Element {
                        CacheOriginX = i * SubGridTreeConsts.SubGridTreeDimension, CacheOriginY = j * SubGridTreeConsts.SubGridTreeDimension, SizeInBytes = 1
                    });
                }
            }

            var additionComplete     = false;
            var invalidationComplete = false;

            // Progressively add and invalidate the elements in the cache in separate tasks

            var additionTask = Task.Run(() => {
                for (var loopCount = 0; loopCount < 100; loopCount++)
                {
                    for (var i = 0; i < 100; i++)
                    {
                        for (var j = 0; j < 100; j++)
                        {
                            for (var contextIndex = 0; contextIndex < contexts.Length; contextIndex++)
                            {
                                if (cache.Get(contexts[contextIndex], i, j) == null)
                                {
                                    cache.Add(contexts[contextIndex], items[contextIndex][i, j], contexts[contextIndex].InvalidationVersion);
                                }
                            }
                        }
                    }
                }

                additionComplete = true;
            });

            var invalidationTask = Task.Run(() =>
            {
                for (var loopCount = 0; loopCount < 100; loopCount++)
                {
                    for (var i = 99; i >= 0; i--)
                    {
                        for (var j = 99; j >= 0; j--)
                        {
                            foreach (var context in contexts)
                            {
                                // Empty contexts are ignored
                                if (context.TokenCount > 0)
                                {
                                    context.InvalidateSubGrid(i * SubGridTreeConsts.SubGridTreeDimension, j * SubGridTreeConsts.SubGridTreeDimension, out var subGridPresentForInvalidation);
                                }
                            }
                        }
                    }
                }

                invalidationComplete = true;
            });

            var numDesignInvalidations = 0;
            var designUpdateTask       = Task.Run(async() =>
            {
                while (!additionComplete || !invalidationComplete)
                {
                    // Mimic design invalidation by periodically invalidating all sub grids
                    await Task.Delay(10);
                    contexts[numDesignInvalidations % contexts.Length].InvalidateAllSubGrids();

                    numDesignInvalidations++;
                }
            });

            Task.WaitAll(new[] { additionTask, invalidationTask, designUpdateTask });

            numDesignInvalidations.Should().BeGreaterThan(0);
        }