Example #1
0
        protected Demo(DemosGame game)
        {
            Game = game;
            parallelLooper = new ParallelLooper();
            //This section lets the engine know that it can make use of multithreaded systems
            //by adding threads to its thread pool.
            #if XBOX360
            parallelLooper.AddThread(delegate { Thread.CurrentThread.SetProcessorAffinity(new[] { 1 }); });
            parallelLooper.AddThread(delegate { Thread.CurrentThread.SetProcessorAffinity(new[] { 3 }); });
            parallelLooper.AddThread(delegate { Thread.CurrentThread.SetProcessorAffinity(new[] { 4 }); });
            parallelLooper.AddThread(delegate { Thread.CurrentThread.SetProcessorAffinity(new[] { 5 }); });

            #else
            if (Environment.ProcessorCount > 1)
            {
                for (int i = 0; i < Environment.ProcessorCount; i++)
                {
                    parallelLooper.AddThread();
                }
            }
            #endif

            Space = new Space(parallelLooper);

            game.Camera.LockedUp = Vector3.Up;
            game.Camera.ViewDirection = new Vector3(0, 0, -1);
        }
Example #2
0
 public void BuildWorld()
 {
     MainThread = Thread.CurrentThread;
     MainThreadID = MainThread.ManagedThreadId;
     ParallelLooper pl = new ParallelLooper();
     for (int i = 0; i < Environment.ProcessorCount; i++)
     {
         pl.AddThread();
     }
     CollisionDetectionSettings.AllowedPenetration = 0.01f;
     PhysicsWorld = new Space(pl);
     PhysicsWorld.TimeStepSettings.MaximumTimeStepsPerFrame = 10;
     PhysicsWorld.ForceUpdater.Gravity = new Vector3(0, 0, -9.8f * 3f / 2f);
     PhysicsWorld.DuringForcesUpdateables.Add(new LiquidVolume(this));
     PhysicsWorld.TimeStepSettings.TimeStepDuration = 1f / TheServer.CVars.g_fps.ValueF;
     Collision = new CollisionUtil(PhysicsWorld);
     EntityConstructors.Add(EntityType.ITEM, new ItemEntityConstructor());
     EntityConstructors.Add(EntityType.BLOCK_ITEM, new BlockItemEntityConstructor());
     EntityConstructors.Add(EntityType.GLOWSTICK, new GlowstickEntityConstructor());
     EntityConstructors.Add(EntityType.MODEL, new ModelEntityConstructor());
     EntityConstructors.Add(EntityType.SMOKE_GRENADE, new SmokeGrenadeEntityConstructor());
     EntityConstructors.Add(EntityType.MUSIC_BLOCK, new MusicBlockEntityConstructor());
     ChunkManager = new ChunkDataManager();
     ChunkManager.Init(this);
     //LoadRegion(new Location(-MaxViewRadiusInChunks * 30), new Location(MaxViewRadiusInChunks * 30), true);
     //TheServer.Schedule.RunAllSyncTasks(0.016); // TODO: Separate per-region scheduler // Also don't freeze the entire server/region just because we're waiting on chunks >.>
     //SysConsole.Output(OutputType.INIT, "Finished building chunks! Now have " + LoadedChunks.Count + " chunks!");
 }
        /// <summary>
        /// Constructs a new demo.
        /// </summary>
        /// <param name="game">Game owning this demo.</param>
        public MultithreadedScalingTestDemo(DemosGame game)
            : base(game)
        {
            simulationBuilders = new Func<Space, int>[]
                                     {
                                         BuildPileSimulation,
                                         BuildWallSimulation,
                                         BuildPlanetSimulation
                                     };

            #if WINDOWS
            int coreCountMax = Environment.ProcessorCount;

            testResults = new double[coreCountMax, simulationBuilders.Length];

            int reruns = 1;
            for (int i = 0; i < reruns; i++)
            {
                GC.Collect();
                var looper = new ParallelLooper();

                //Try different thread counts.
                for (int j = 0; j < coreCountMax; j++)
                {
                    looper.AddThread();
                    for (int k = 0; k < simulationBuilders.Length; k++)
                        testResults[j, k] = RunTest(looper, simulationBuilders[k]);
                    GC.Collect();

                }
            }
            #else
            testResults = new double[4, simulationBuilders.Length];
            int reruns = 10;
            for (int i = 0; i < reruns; i++)
            {
                GC.Collect();
                var threadManager = new SpecializedThreadManager();

                threadManager.AddThread(delegate { Thread.CurrentThread.SetProcessorAffinity(new[] { 1 }); }, null);

                for (int k = 0; k < simulationBuilders.Length; k++)
                    testResults[0, k] += RunTest(threadManager, simulationBuilders[k]);
                GC.Collect();

                threadManager.AddThread(delegate { Thread.CurrentThread.SetProcessorAffinity(new[] { 3 }); }, null);
                for (int k = 0; k < simulationBuilders.Length; k++)
                    testResults[1, k] += RunTest(threadManager, simulationBuilders[k]);
                GC.Collect();
                threadManager.AddThread(delegate { Thread.CurrentThread.SetProcessorAffinity(new[] { 5 }); }, null);
                for (int k = 0; k < simulationBuilders.Length; k++)
                    testResults[2, k] += RunTest(threadManager, simulationBuilders[k]);
                GC.Collect();
                threadManager.AddThread(delegate { Thread.CurrentThread.SetProcessorAffinity(new[] { 4 }); }, null);
                for (int k = 0; k < simulationBuilders.Length; k++)
                    testResults[3, k] += RunTest(threadManager, simulationBuilders[k]);
                GC.Collect();
            }
            #endif
        }
        internal ParallelLoopWorker(ParallelLooper manager, Action threadStart)
        {
            this.manager = manager;
            this.threadStart = threadStart;

            getToWork = new AutoResetEvent(false);

            thread = new Thread(Work) { IsBackground = true };
            thread.Start();
        }
Example #5
0
        internal ParallelLoopWorker(ParallelLooper manager, Action threadStart)
        {
            this.manager     = manager;
            this.threadStart = threadStart;

            getToWork = new AutoResetEvent(false);

            thread = new Thread(Work)
            {
                IsBackground = true
            };
            thread.Start();
        }
        /// <summary>
        /// Constructs a new demo.
        /// </summary>
        /// <param name="game">Game owning this demo.</param>
        public BroadPhaseMultithreadingTestDemo(DemosGame game)
            : base(game)
        {
            Space.Solver.IterationLimit = 0;


#if WINDOWS
            int coreCountMax = Environment.ProcessorCount;
            int splitOffsetMax = 6;
            testResults = new double[coreCountMax, splitOffsetMax + 1];

            int reruns = 10;
            for (int i = 0; i < reruns; i++)
            {
                GC.Collect();
                var looper = new ParallelLooper();



                //Try different thread counts.
                for (int j = 0; j < coreCountMax; j++)
                {
                    looper.AddThread();
                    //Try different split levels.);
                    for (int k = 0; k <= splitOffsetMax; k++)
                    {
                        testResults[j, k] += RunTest(k, looper);
                        GC.Collect();
                    }
                }
            }

            for (int i = 0; i < testResults.GetLength(0); i++)
            {
                for (int j = 0; j < testResults.GetLength(1); j++)
                {
                    testResults[i, j] /= reruns;
                }
            }
#else
            int splitOffsetMax = 6;

            testResults = new double[4, splitOffsetMax + 1];

            int reruns = 10;
            for (int i = 0; i < reruns; i++)
            {
                var threadManager = new SpecializedThreadManager();
                GC.Collect();

                threadManager.AddThread(delegate { Thread.CurrentThread.SetProcessorAffinity(new[] { 1 }); }, null);
                for (int j = 0; j <= splitOffsetMax; j++)
                {
                    testResults[0, j] += RunTest(j, threadManager);
                    GC.Collect();
                }
                threadManager.AddThread(delegate { Thread.CurrentThread.SetProcessorAffinity(new[] { 3 }); }, null);
                for (int j = 0; j <= splitOffsetMax; j++)
                {
                    testResults[1, j] += RunTest(j, threadManager);
                    GC.Collect();
                }
                threadManager.AddThread(delegate { Thread.CurrentThread.SetProcessorAffinity(new[] { 5 }); }, null);
                for (int j = 0; j <= splitOffsetMax; j++)
                {
                    testResults[2, j] += RunTest(j, threadManager);
                    GC.Collect();
                }
                threadManager.AddThread(delegate { Thread.CurrentThread.SetProcessorAffinity(new[] { 4 }); }, null);
                for (int j = 0; j <= splitOffsetMax; j++)
                {
                    testResults[3, j] += RunTest(j, threadManager);
                    GC.Collect();
                }
            }

            for (int i = 0; i < testResults.GetLength(0); i++)
            {
                for (int j = 0; j < testResults.GetLength(1); j++)
                {
                    testResults[i,j] /= reruns;
                }
            }
#endif


        }
Example #7
0
 /// <summary>
 /// Builds the physics world.
 /// </summary>
 public void BuildWorld()
 {
     ParallelLooper pl = new ParallelLooper();
     for (int i = 0; i < Environment.ProcessorCount; i++)
     {
         pl.AddThread();
     }
     CollisionDetectionSettings.AllowedPenetration = 0.01f;
     PhysicsWorld = new Space(pl);
     PhysicsWorld.TimeStepSettings.MaximumTimeStepsPerFrame = 10;
     // Set the world's general default gravity
     PhysicsWorld.ForceUpdater.Gravity = new BEPUutilities.Vector3(0, 0, -9.8f * 3f / 2f);
     PhysicsWorld.DuringForcesUpdateables.Add(new LiquidVolume(this));
     // Load a CollisionUtil instance
     Collision = new CollisionUtil(PhysicsWorld);
 }
Example #8
0
        public static void Test()
        {
            float leafMinSize = 1;
            float leafMaxSize = 100;
            float leafSizePower = 10;
            int queryCount = 1000000;
            int selfTestCount = 1;
            int refitCount = 1;
            int frameCount = 2048;
            float dt = 1 / 60f;

            VelocityDescription velocityDescription = new VelocityDescription
            {
                MinVelocity = 0,
                MaxVelocity = 10,
                VelocityDistributionPower = 10,
                PortionOfMovingLeaves = 1
            };

            Vector3 querySize = new Vector3(20);
            int queryLocationCount = 16384; //<-- POWER OF TWO!!! REMEMBER!

            ParallelLooper looper = new ParallelLooper();
            for (int i = 0; i < Environment.ProcessorCount; ++i)
            {
                looper.AddThread();
            }


#if RANDOMLEAVES
            BoundingBox randomLeafBounds = new BoundingBox { Min = new Vector3(0, 0, 0), Max = new Vector3(629.96f) };
            BoundingBox queryBounds = randomLeafBounds;
            int randomLeafCount = 65536;

#else
            int leafCountX = 64;
            int leafCountY = 64;
            int leafCountZ = 64;
            float leafGap = 10;
            BoundingBox queryBounds = new BoundingBox { Min = new Vector3(0), Max = new Vector3(leafCountX, leafCountY, leafCountZ) * (new Vector3(leafSize) + new Vector3(leafGap)) };
#endif

            {

                var queries = GetQueryLocations(queryLocationCount, queryBounds, querySize);

#if RANDOMLEAVES
                var leaves = GetRandomLeaves(randomLeafCount, randomLeafBounds, new Vector3(leafMinSize), new Vector3(leafMaxSize), leafSizePower, velocityDescription);
#else
                var leaves = GetLeaves(leafCountX, leafCountY, leafCountZ, leafSize, leafGap);
#endif
                GC.Collect();
                //TestVectorized(leaves, queries, queryCount, selfTestCount, refitCount);
#if RANDOMLEAVES
                leaves = GetRandomLeaves(randomLeafCount, randomLeafBounds, new Vector3(leafMinSize), new Vector3(leafMaxSize), leafSizePower, velocityDescription);
#else
                leaves = GetLeaves(leafCountX, leafCountY, leafCountZ, leafSize, leafGap);
#endif
                GC.Collect();
                //TestBaseline(leaves, queries, queryCount, selfTestCount, refitCount);
#if RANDOMLEAVES
                leaves = GetRandomLeaves(randomLeafCount, randomLeafBounds, new Vector3(leafMinSize), new Vector3(leafMaxSize), leafSizePower, velocityDescription);
#else
                leaves = GetLeaves(leafCountX, leafCountY, leafCountZ, leafSize, leafGap);
#endif
                GC.Collect();
                var results = TestSingleArray(leaves, queries, randomLeafBounds, queryCount, selfTestCount, refitCount, frameCount, dt, looper);

                using (var stream = File.Open("newTreeResults.txt", FileMode.Create))
                {
                    using (var textWriter = new StreamWriter(stream))
                    {
                        results.Save(textWriter);
                    }
                }
            }

            {

#if RANDOMLEAVES
                var leaves = GetRandomLeavesBEPU(randomLeafCount, randomLeafBounds, new Vector3(leafMinSize), new Vector3(leafMaxSize), leafSizePower, velocityDescription);
#else
                var leaves = GetLeavesBEPU(leafCountX, leafCountY, leafCountZ, leafSize, leafGap);
#endif
                var queries = GetBEPUQueryLocations(queryLocationCount, queryBounds, querySize);

                GC.Collect();
                //TestBEPU(leaves, queries, queryCount, selfTestCount, refitCount);
                var results = TestDH(leaves, queries, ref randomLeafBounds, queryCount, selfTestCount, refitCount, frameCount, dt, looper);
                using (var stream = File.Open("oldDHResults.txt", FileMode.Create))
                {
                    using (var textWriter = new StreamWriter(stream))
                    {
                        results.Save(textWriter);
                    }
                }
            }

        }
        public static unsafe TestResults TestSingleArray(TestCollidable[] leaves, BoundingBox[] queries, BoundingBox positionBounds,
            int queryCount, int selfTestCount, int refitCount, int frameCount, float dt, ParallelLooper looper)
        {
            {

                var warmLeaves = GetLeaves(10, 10, 10, 10, 10);
                Tree tree = new Tree();
                //for (int i = 0; i < leaves.Length; ++i)
                //{
                //    BoundingBox box;
                //    leaves[i].GetBoundingBox(out box);
                //    //tree.Insert(i, ref box);
                //    tree.AddGlobal(i, ref box);
                //}
                int[] leafIds = new int[warmLeaves.Length];
                BoundingBox[] leafBounds = new BoundingBox[warmLeaves.Length];
                for (int i = 0; i < warmLeaves.Length; ++i)
                {
                    leafIds[i] = i;
                    warmLeaves[i].GetBoundingBox(out leafBounds[i]);
                }
                //tree.BuildMedianSplit(leafIds, leafBounds);
                //tree.BuildVolumeHeuristic(leafIds, leafBounds);
                tree.SweepBuild(leafIds, leafBounds);
                Console.WriteLine($"SingleArray Cachewarm Build: {tree.LeafCount}");

                tree.Refit();
                //tree.BottomUpAgglomerativeRefine();
                //tree.TopDownAgglomerativeRefine();
                //tree.BottomUpSweepRefine();
                //tree.TopDownSweepRefine();

                tree.RefitAndRefine(0);
                var context = new Tree.RefitAndRefineMultithreadedContext(tree);
                tree.RefitAndRefine(0, looper, context);

                var selfTestContext = new Tree.SelfTestMultithreadedContext(looper.ThreadCount, BufferPools<Overlap>.Locking);
                tree.GetSelfOverlaps(looper, selfTestContext);

                var list = new QuickList<int>(new BufferPool<int>());
                BoundingBox aabb = new BoundingBox { Min = new Vector3(0, 0, 0), Max = new Vector3(1, 1, 1) };
                tree.QueryRecursive(ref aabb, ref list);
                list.Dispose();

                var overlaps = new QuickList<Overlap>(new BufferPool<Overlap>());
                tree.GetSelfOverlaps(ref overlaps);

                overlaps = new QuickList<Overlap>(new BufferPool<Overlap>());
                tree.GetSelfOverlapsArityDedicated(ref overlaps);

                tree.IncrementalCacheOptimize(0);

                overlaps = new QuickList<Overlap>(new BufferPool<Overlap>());

                tree.GetSelfOverlapsViaQueries(ref overlaps);
                Console.WriteLine($"Cachewarm overlaps: {overlaps.Count}");
                tree.Dispose();

            }

            {
                Console.WriteLine($"SingleArray arity: {Tree.ChildrenCapacity}");
                Tree tree = new Tree(Math.Max(1, leaves.Length));
                var startTime = Stopwatch.GetTimestamp() / (double)Stopwatch.Frequency;
                for (int i = 0; i < leaves.Length; ++i)
                {
                    var leafIndex = (int)((982451653L * i) % leaves.Length);
                    BoundingBox box;
                    leaves[leafIndex].GetBoundingBox(out box);
                    tree.Add(leafIndex, ref box);
                    //tree.AddGlobal(leafIndex, ref box);
                }
                //int[] leafIds = new int[leaves.Length];
                //BoundingBox[] leafBounds = new BoundingBox[leaves.Length];
                //for (int i = 0; i < leaves.Length; ++i)
                //{
                //    leafIds[i] = i;
                //    leaves[i].GetBoundingBox(out leafBounds[i]);
                //}
                ////tree.BuildMedianSplit(leafIds, leafBounds);
                ////tree.BuildVolumeHeuristic(leafIds, leafBounds);
                //tree.SweepBuild(leafIds, leafBounds);
                var endTime = Stopwatch.GetTimestamp() / (double)Stopwatch.Frequency;
                Console.WriteLine($"SingleArray Build Time: {endTime - startTime}, depth: {tree.ComputeMaximumDepth()}");

                int nodeCount, childCount;
                tree.MeasureNodeOccupancy(out nodeCount, out childCount);
                Console.WriteLine($"SingleArray Occupancy: {childCount / (double)nodeCount}");
                Console.WriteLine($"Cost metric: {tree.MeasureCostMetric()}");
                Console.WriteLine($"Cache Quality: {tree.MeasureCacheQuality()}");

                tree.Validate();

                startTime = Stopwatch.GetTimestamp() / (double)Stopwatch.Frequency;
                for (int i = 0; i < refitCount; ++i)
                {
                    //for (int i = 0; i < tree.LeafCount; ++i)
                    //{
                    //    BoundingBox box;
                    //    leaves[tree.Leaves[i].Id].GetBoundingBox(out box);
                    //    tree.UpdateLeafBoundingBox(i, ref box);
                    //}
                    tree.Refit();
                }
                endTime = Stopwatch.GetTimestamp() / (double)Stopwatch.Frequency;
                Console.WriteLine($"SingleArray Refit Time1: {endTime - startTime}");

                var overlaps = new QuickList<Overlap>(new BufferPool<Overlap>());
                startTime = Stopwatch.GetTimestamp() / (double)Stopwatch.Frequency;
                for (int i = 0; i < selfTestCount; ++i)
                {
                    overlaps.Count = 0;
                    tree.GetSelfOverlaps(ref overlaps);
                }
                endTime = Stopwatch.GetTimestamp() / (double)Stopwatch.Frequency;
                Console.WriteLine($"SingleArray SelfTree Time1: {endTime - startTime}, overlaps: {overlaps.Count}");

                int[] buffer;
                MemoryRegion region;
                BinnedResources resources;
                const int maximumSubtrees = 262144;
                var spareNodes = new QuickList<int>(new BufferPool<int>(), 8);
                var subtreeReferences = new QuickList<int>(BufferPools<int>.Thread, BufferPool<int>.GetPoolIndex(maximumSubtrees));
                var treeletInternalNodes = new QuickList<int>(BufferPools<int>.Thread, BufferPool<int>.GetPoolIndex(maximumSubtrees));
                Tree.CreateBinnedResources(BufferPools<int>.Thread, maximumSubtrees, out buffer, out region, out resources);
                bool nodesInvalidated;
                overlaps = new QuickList<Overlap>(new BufferPool<Overlap>());

                var refineContext = new Tree.RefitAndRefineMultithreadedContext(tree);
                var selfTestContext = new Tree.SelfTestMultithreadedContext(looper.ThreadCount, BufferPools<Overlap>.Locking);

                var visitedNodes = new QuickSet<int>(BufferPools<int>.Thread, BufferPools<int>.Thread);

                //**************** Dynamic Testing
                Random random = new Random(5);
                TestResults results = new TestResults("New", frameCount);
                startTime = Stopwatch.GetTimestamp() / (double)Stopwatch.Frequency;
                for (int t = 0; t < frameCount; ++t)
                {
                    //Update the positions of objects.
                    for (int i = 0; i < tree.LeafCount; ++i)
                    {
                        var leafId = tree.Leaves[i].Id;
                        var leaf = leaves[leafId];

                        //Bounce off the walls.
                        if (leaf.Position.X < positionBounds.Min.X && leaf.Velocity.X < 0)
                            leaf.Velocity.X = -leaf.Velocity.X;
                        if (leaf.Position.Y < positionBounds.Min.Y && leaf.Velocity.Y < 0)
                            leaf.Velocity.Y = -leaf.Velocity.Y;
                        if (leaf.Position.Z < positionBounds.Min.Z && leaf.Velocity.Z < 0)
                            leaf.Velocity.Z = -leaf.Velocity.Z;

                        if (leaf.Position.X > positionBounds.Max.X && leaf.Velocity.X > 0)
                            leaf.Velocity.X = -leaf.Velocity.X;
                        if (leaf.Position.Y > positionBounds.Max.Y && leaf.Velocity.Y > 0)
                            leaf.Velocity.Y = -leaf.Velocity.Y;
                        if (leaf.Position.Z > positionBounds.Max.Z && leaf.Velocity.Z > 0)
                            leaf.Velocity.Z = -leaf.Velocity.Z;

                        leaf.Position += leaf.Velocity * dt;
                        BoundingBox boundingBox;
                        leaf.GetBoundingBox(out boundingBox);
                        tree.SetLeafBoundingBox(i, ref boundingBox);
                    }
                    var refineStartTime = Stopwatch.GetTimestamp() / (double)Stopwatch.Frequency;

                    int refinementCount;
                    if(looper.ThreadCount > 1)
                        refinementCount = tree.RefitAndRefine(t, looper, refineContext);
                    else
                        refinementCount = tree.RefitAndRefine(t);

                    var refineEndTime = Stopwatch.GetTimestamp() / (double)Stopwatch.Frequency;

                    int overlapsCount;
                    if (looper.ThreadCount > 1)
                    {
                        tree.GetSelfOverlaps(looper, selfTestContext);
                        overlapsCount = 0;
                        for (int i = 0; i < selfTestContext.WorkerOverlaps.Length; ++i)
                        {
                            overlapsCount += selfTestContext.WorkerOverlaps[i].Count;
                        }
                    }
                    else
                    {
                        overlaps.Count = 0;
                        tree.GetSelfOverlapsArityDedicated(ref overlaps);
                        overlapsCount = overlaps.Count;
                    }

                    var testEndTime = Stopwatch.GetTimestamp() / (double)Stopwatch.Frequency;

                    results.Refine[t] = 1000 * (refineEndTime - refineStartTime);
                    results.SelfTest[t] = 1000 * (testEndTime - refineEndTime);
                    results.Total[t] = 1000 * (testEndTime - refineStartTime);
                    results.OverlapCounts[t] = overlapsCount;
                    results.TreeCosts[t] = tree.MeasureCostMetric();

                    if (t % 16 == 0)
                    {
                        Console.WriteLine($"_________________{t}_________________");
                        Console.WriteLine($"Refinement count: {refinementCount}");
                        Console.WriteLine($"Refine time:      {results.Refine[t]}");
                        Console.WriteLine($"Test time:        {results.SelfTest[t]}");
                        Console.WriteLine($"TIME:             {results.Total[t]}");
                        Console.WriteLine($"Cost metric:      {results.TreeCosts[t]}");
                        Console.WriteLine($"Overlaps:         {results.OverlapCounts[t]}");
                        Console.WriteLine($"Cache Quality:    {tree.MeasureCacheQuality()}");
                        GC.Collect();
                    }
                    tree.Validate();
                }
                endTime = Stopwatch.GetTimestamp() / (double)Stopwatch.Frequency;
                tree.Validate();
                Console.WriteLine($"SingleArray Cache Quality: {tree.MeasureCacheQuality()}");
                Console.WriteLine($"Cost metric: {tree.MeasureCostMetric()}");

                region.Dispose();
                tree.RemoveUnusedInternalNodes(ref spareNodes);
                BufferPools<int>.Thread.GiveBack(buffer);

                //********************

                tree.MeasureNodeOccupancy(out nodeCount, out childCount);
                Console.WriteLine($"SingleArray Occupancy: {childCount / (double)nodeCount}");

                startTime = Stopwatch.GetTimestamp() / (double)Stopwatch.Frequency;
                for (int i = 0; i < refitCount; ++i)
                {
                    //for (int i = 0; i < tree.LeafCount; ++i)
                    //{
                    //    BoundingBox box;
                    //    leaves[tree.Leaves[i].Id].GetBoundingBox(out box);
                    //    tree.UpdateLeafBoundingBox(i, ref box);
                    //}
                    tree.Refit();
                }
                endTime = Stopwatch.GetTimestamp() / (double)Stopwatch.Frequency;
                Console.WriteLine($"SingleArray Refit Time2: {endTime - startTime}");

                var list = new QuickList<int>(new BufferPool<int>());
                var queryMask = queries.Length - 1;
                startTime = Stopwatch.GetTimestamp() / (double)Stopwatch.Frequency;
                for (int i = 0; i < queryCount; ++i)
                {
                    list.Count = 0;
                    //tree.Query2(ref queries[i & queryMask], ref list);
                    tree.QueryRecursive(ref queries[i & queryMask], ref list);
                }
                endTime = Stopwatch.GetTimestamp() / (double)Stopwatch.Frequency;
                Console.WriteLine($"SingleArray Query Time: {endTime - startTime}, overlaps: {list.Count}");
                Array.Clear(list.Elements, 0, list.Elements.Length);
                list.Dispose();

                startTime = Stopwatch.GetTimestamp() / (double)Stopwatch.Frequency;
                for (int i = 0; i < selfTestCount; ++i)
                {
                    overlaps.Count = 0;
                    tree.GetSelfOverlaps(ref overlaps);
                }
                endTime = Stopwatch.GetTimestamp() / (double)Stopwatch.Frequency;
                Console.WriteLine($"SingleArray SelfTree Time: {endTime - startTime}, overlaps: {overlaps.Count}");

                startTime = Stopwatch.GetTimestamp() / (double)Stopwatch.Frequency;
                for (int i = 0; i < selfTestCount; ++i)
                {
                    overlaps.Count = 0;
                    tree.GetSelfOverlapsArityDedicated(ref overlaps);
                }
                endTime = Stopwatch.GetTimestamp() / (double)Stopwatch.Frequency;
                Console.WriteLine($"SingleArray Arity-Dedicated SelfTree Time: {endTime - startTime}, overlaps: {overlaps.Count}");

                startTime = Stopwatch.GetTimestamp() / (double)Stopwatch.Frequency;
                for (int i = 0; i < selfTestCount; ++i)
                {
                    overlaps.Count = 0;
                    tree.GetSelfOverlapsViaQueries(ref overlaps);
                }
                endTime = Stopwatch.GetTimestamp() / (double)Stopwatch.Frequency;
                Console.WriteLine($"SingleArray SelfQuery Time: {endTime - startTime}, overlaps: {overlaps.Count}");

                tree.Dispose();

                return results;
            }
        }