static void Subtest <TBodyBuilder, TConstraintBuilder, TConstraint>(TBodyBuilder bodyBuilder, TConstraintBuilder constraintBuilder, int width, int height, int length, int frameCount, IThreadDispatcher initializationThreadPool, StreamWriter writer) where TBodyBuilder : IBodyBuilder where TConstraintBuilder : IConstraintBuilder where TConstraint : IConstraintDescription <TConstraint> { const int testsPerVariant = 8; WriteLine(writer, $"{width}x{height}x{length} lattice, {frameCount} frames:"); var timings = new SimulationTimeSamples[Environment.ProcessorCount]; //for (int threadCount = 1; threadCount <= 1; ++threadCount) for (int threadCount = 1; threadCount <= Environment.ProcessorCount; ++threadCount) { var threadPool = new SimpleThreadDispatcher(threadCount); SimulationTimeSamples bestTimings = null; double bestTime = double.MaxValue; for (int i = 0; i < testsPerVariant; ++i) { var candidateTimings = Solve <TBodyBuilder, TConstraintBuilder, TConstraint>(bodyBuilder, constraintBuilder, width, height, length, frameCount, threadCount, initializationThreadPool, threadPool); var totalStats = candidateTimings.Simulation.ComputeStats(); WriteLine(writer, $"{i} AVE: {Math.Round(1e3 * totalStats.Average, 2)}, " + $"MIN: {Math.Round(1e3 * totalStats.Min, 2)}, " + $"MAX: {Math.Round(1e3 * totalStats.Max, 2)}, " + $"STD DEV: {Math.Round(1e3 * totalStats.StdDev, 3)}, "); if (totalStats.Total < bestTime) { bestTime = totalStats.Total; bestTimings = candidateTimings; } } WriteLine(writer, $"{threadCount}T: " + $"{Math.Round(bestTime * 1e3, 2)}, " + $"BOPT: {Math.Round(bestTimings.BodyOptimizer.ComputeStats().Total * 1e3, 2)}, " + $"COPT: {Math.Round(bestTimings.ConstraintOptimizer.ComputeStats().Total * 1e3, 2)}, " + $"BCMP: {Math.Round(bestTimings.BatchCompressor.ComputeStats().Total * 1e3, 2)}, " + $"POIN: {Math.Round(bestTimings.PoseIntegrator.ComputeStats().Total * 1e3, 2)}, " + $"SOLV: {Math.Round(bestTimings.Solver.ComputeStats().Total * 1e3, 2)}"); timings[threadCount - 1] = bestTimings; threadPool.Dispose(); } int fastestIndex = 0; for (int i = 1; i < timings.Length; ++i) { if (timings[i].Simulation.ComputeStats().Total < timings[fastestIndex].Simulation.ComputeStats().Total) { fastestIndex = i; } } WriteLine(writer, $"Scaling: {timings[0].Simulation.ComputeStats().Total / timings[fastestIndex].Simulation.ComputeStats().Total}"); }
public static void Test() { var memoryStream = new MemoryStream(); var writer = new StreamWriter(memoryStream); var initializationThreadPool = new SimpleThreadDispatcher(Environment.ProcessorCount); var bodyBuilder = new RegularGridWithKinematicBaseBuilder(new Vector3(1), new Vector3()); var constraintBuilder = new ContactManifoldConstraintBuilder(); //Subtest<RegularGridWithKinematicBaseBuilder, ContactManifoldConstraintBuilder, ContactManifold4Constraint>(bodyBuilder, constraintBuilder, 32, 32, 32, 8, initializationThreadPool, writer); //Subtest<RegularGridWithKinematicBaseBuilder, ContactManifoldConstraintBuilder, ContactManifold4Constraint>(bodyBuilder, constraintBuilder, 26, 26, 26, 12, initializationThreadPool, writer); //Subtest<RegularGridWithKinematicBaseBuilder, ContactManifoldConstraintBuilder, ContactManifold4Constraint>(bodyBuilder, constraintBuilder, 20, 20, 20, 20, initializationThreadPool, writer); //Subtest<RegularGridWithKinematicBaseBuilder, ContactManifoldConstraintBuilder, ContactManifold4Constraint>(bodyBuilder, constraintBuilder, 16, 16, 16, 30, initializationThreadPool, writer); //Subtest<RegularGridWithKinematicBaseBuilder, ContactManifoldConstraintBuilder, ContactManifold4Constraint>(bodyBuilder, constraintBuilder, 13, 13, 13, 45, initializationThreadPool, writer); Subtest <RegularGridWithKinematicBaseBuilder, ContactManifoldConstraintBuilder, Contact4>(bodyBuilder, constraintBuilder, 10, 10, 10, 700, initializationThreadPool, writer); //var bodyBuilder = new RegularGridWithKinematicBaseBuilder(new Vector3(1), new Vector3()); //var constraintBuilder = new BallSocketConstraintBuilder(); //Subtest<RegularGridWithKinematicBaseBuilder, BallSocketConstraintBuilder, BallSocket>(bodyBuilder, constraintBuilder, 32, 32, 32, 8, initializationThreadPool, writer); //Subtest<RegularGridWithKinematicBaseBuilder, BallSocketConstraintBuilder, BallSocket>(bodyBuilder, constraintBuilder, 26, 26, 26, 12, initializationThreadPool, writer); //Subtest<RegularGridWithKinematicBaseBuilder, BallSocketConstraintBuilder, BallSocket>(bodyBuilder, constraintBuilder, 20, 20, 20, 20, initializationThreadPool, writer); //Subtest<RegularGridWithKinematicBaseBuilder, BallSocketConstraintBuilder, BallSocket>(bodyBuilder, constraintBuilder, 16, 16, 16, 30, initializationThreadPool, writer); //Subtest<RegularGridWithKinematicBaseBuilder, BallSocketConstraintBuilder, BallSocket>(bodyBuilder, constraintBuilder, 13, 13, 13, 45, initializationThreadPool, writer); //Subtest<RegularGridWithKinematicBaseBuilder, BallSocketConstraintBuilder, BallSocket>(bodyBuilder, constraintBuilder, 10, 10, 10, 70, initializationThreadPool, writer); initializationThreadPool.Dispose(); writer.Flush(); var path = "log.txt"; using (var stream = File.OpenWrite(path)) { Console.WriteLine($"Writing results to path: {Path.GetFullPath(path)}"); var bytes = memoryStream.ToArray(); stream.Write(bytes, 0, bytes.Length); } Console.ReadKey(); }
public static void Test() { const int width = 32; const int height = 32; const int length = 32; SimulationSetup.BuildLattice( new RegularGridWithKinematicBaseBuilder(new Vector3(1), new Vector3()), new BallSocketConstraintBuilder(), width, height, length, out var simulation, out var bodyHandles, out var constraintHandles); var threadDispatcher = new SimpleThreadDispatcher(8); const float inverseDt = 60f; const float dt = 1 / inverseDt; const int iterationCount = 8; const int frameCount = 128; simulation.Solver.IterationCount = iterationCount; simulation.PoseIntegrator.Gravity = new Vector3(0, -10, 0); var samples = new SimulationTimeSamples(frameCount); for (int frameIndex = 0; frameIndex < frameCount; ++frameIndex) { var energyBefore = TestHelpers.GetBodyEnergyHeuristic(simulation.Bodies); //simulation.Timestep(dt); simulation.Timestep(dt, threadDispatcher); samples.RecordFrame(simulation); var energyAfter = TestHelpers.GetBodyEnergyHeuristic(simulation.Bodies); int sampledBodyIndex = width; var samplePose = simulation.Bodies.ActiveSet.Poses[sampledBodyIndex]; var sampleVelocity = simulation.Bodies.ActiveSet.Velocities[sampledBodyIndex]; //for (int i =0; i < simulation.Bodies.BodyCount; ++i) //{ // simulation.Bodies.GetPose(simulation.Bodies.IndexToHandle[i], out var pose); // simulation.Bodies.GetVelocity(simulation.Bodies.IndexToHandle[i], out var velocity); // Console.WriteLine($"Sample {i} position: {pose.Position}, velocity: {velocity.Linear}"); //} Console.WriteLine($"Sample {sampledBodyIndex} position: {samplePose.Position}, velocity: {sampleVelocity.Linear}"); Console.WriteLine($"Body energy {frameIndex}: {energyAfter}, delta: {energyAfter - energyBefore}"); } var multiplier = 1e3 / frameCount; Console.WriteLine($"Simulation time (ms): {multiplier * samples.Simulation.ComputeStats().Total}"); Console.WriteLine($"Body opt time (ms): {multiplier * samples.BodyOptimizer.ComputeStats().Total}"); Console.WriteLine($"Constraint opt time (ms): {multiplier * samples.ConstraintOptimizer.ComputeStats().Total}"); Console.WriteLine($"Batch compress time (ms): {multiplier * samples.BatchCompressor.ComputeStats().Total}"); Console.WriteLine($"Pose integrate time (ms): {multiplier * samples.PoseIntegrator.ComputeStats().Total}"); Console.WriteLine($"Solve time (ms): {multiplier * samples.Solver.ComputeStats().Total}"); threadDispatcher.Dispose(); simulation.BufferPool.Clear(); }
public static void Test() { var pool = new BufferPool(); var tree = new Tree(pool, 128); const int leafCountAlongXAxis = 11; const int leafCountAlongYAxis = 13; const int leafCountAlongZAxis = 15; var leafCount = leafCountAlongXAxis * leafCountAlongYAxis * leafCountAlongZAxis; pool.Take <BoundingBox>(leafCount, out var leafBounds); const float boundsSpan = 2; const float spanRange = 2; const float boundsSpacing = 3; var random = new Random(5); for (int i = 0; i < leafCountAlongXAxis; ++i) { for (int j = 0; j < leafCountAlongYAxis; ++j) { for (int k = 0; k < leafCountAlongZAxis; ++k) { var index = leafCountAlongXAxis * leafCountAlongYAxis * k + leafCountAlongXAxis * j + i; leafBounds[index].Min = new Vector3(i, j, k) * boundsSpacing; leafBounds[index].Max = leafBounds[index].Min + new Vector3(boundsSpan) + spanRange * new Vector3((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()); } } } var prebuiltCount = Math.Max(leafCount / 2, 1); tree.SweepBuild(pool, leafBounds.Slice(0, prebuiltCount)); tree.Validate(); for (int i = prebuiltCount; i < leafCount; ++i) { tree.Add(ref leafBounds[i], pool); } tree.Validate(); pool.Take <int>(leafCount, out var handleToLeafIndex); pool.Take <int>(leafCount, out var leafIndexToHandle); for (int i = 0; i < leafCount; ++i) { handleToLeafIndex[i] = i; leafIndexToHandle[i] = i; } const int iterations = 100000; const int maximumChangesPerIteration = 20; var threadDispatcher = new SimpleThreadDispatcher(Environment.ProcessorCount); var refineContext = new Tree.RefitAndRefineMultithreadedContext(); var selfTestContext = new Tree.MultithreadedSelfTest <OverlapHandler>(pool); var overlapHandlers = new OverlapHandler[threadDispatcher.ThreadCount]; Action <int> pairTestAction = selfTestContext.PairTest; QuickList <int, Buffer <int> > .Create(pool.SpecializeFor <int>(), leafCount, out var removedLeafHandles); for (int i = 0; i < iterations; ++i) { var changeCount = random.Next(maximumChangesPerIteration); for (int j = 0; j <= changeCount; ++j) { var addedFraction = tree.LeafCount / (float)leafCount; if (random.NextDouble() < addedFraction) { //Remove a leaf. var leafIndexToRemove = random.Next(tree.LeafCount); var handleToRemove = leafIndexToHandle[leafIndexToRemove]; var movedLeafIndex = tree.RemoveAt(leafIndexToRemove); if (movedLeafIndex >= 0) { var movedHandle = leafIndexToHandle[movedLeafIndex]; handleToLeafIndex[movedHandle] = leafIndexToRemove; leafIndexToHandle[leafIndexToRemove] = movedHandle; leafIndexToHandle[movedLeafIndex] = -1; } else { //The removed leaf was the last one. This leaf index is no longer associated with any existing leaf. leafIndexToHandle[leafIndexToRemove] = -1; } handleToLeafIndex[handleToRemove] = -1; removedLeafHandles.AddUnsafely(handleToRemove); tree.Validate(); } else { //Add a leaf. var indexInRemovedList = random.Next(removedLeafHandles.Count); var handleToAdd = removedLeafHandles[indexInRemovedList]; removedLeafHandles.FastRemoveAt(indexInRemovedList); var leafIndex = tree.Add(ref leafBounds[handleToAdd], pool); leafIndexToHandle[leafIndex] = handleToAdd; handleToLeafIndex[handleToAdd] = leafIndex; tree.Validate(); } } tree.Refit(); tree.Validate(); tree.RefitAndRefine(pool, i); tree.Validate(); var handler = new OverlapHandler(); tree.GetSelfOverlaps(ref handler); tree.Validate(); refineContext.RefitAndRefine(ref tree, pool, threadDispatcher, i); tree.Validate(); for (int k = 0; k < threadDispatcher.ThreadCount; ++k) { overlapHandlers[k] = new OverlapHandler(); } selfTestContext.PrepareJobs(ref tree, overlapHandlers, threadDispatcher.ThreadCount); threadDispatcher.DispatchWorkers(pairTestAction); selfTestContext.CompleteSelfTest(); tree.Validate(); if (i % 50 == 0) { Console.WriteLine($"Cost: {tree.MeasureCostMetric()}"); Console.WriteLine($"Cache Quality: {tree.MeasureCacheQuality()}"); Console.WriteLine($"Overlap Count: {handler.OverlapCount}"); } } threadDispatcher.Dispose(); pool.Clear(); }