Example #1
0
        IEnumerator MainThreadLoopWithNaiveSynchronization()
        {
            var bounds = new Bounds(_BoundCenter, _BoundSize);

            var syncRunner = new SyncRunner();

            while (_breakIt == false)
            {
                _time = Time.time / 10;
                //Since we are using the SyncRunner, we don't need to yield the execution
                //as the SyncRunner is meant to stall the thread where it starts from.
                //The main thread will be stuck until the multiParallelTask has been
                //executed. A MultiParallelTaskCollection relies on its own
                //internal threads to run, so although the Main thread is stuck
                //the operation will complete
                _multiParallelTasks.ThreadSafeRunOnSchedule(syncRunner);
                //then it resumes here, in the main thread, copying the result to the particleDataBuffer.
                //remember, multiParalleTasks is not executing anymore until the next frame!
                //so the array is safe to use
                _particleDataBuffer.SetData(_gpuparticleDataArr);
                //render the particles. I use DrawMeshInstancedIndirect but
                //there aren't any compute shaders running. This is so cool!
                Graphics.DrawMeshInstancedIndirect(_pointMesh, 0, _material,
                                                   bounds, _GPUInstancingArgsBuffer);
                //continue the cycle on the next frame
                yield return(null);
            }

            //the application is shutting down. This is not that necessary in a
            //standalone client, but necessary to stop the thread when the
            //application is stopped in the Editor to stop all the threads.
            _multiParallelTasks.ClearAndKill();

            TaskRunner.Instance.StopAndCleanupAllDefaultSchedulerTasks();
        }
Example #2
0
    IEnumerator UpdateIT()
    {
        var waitForSecondsEnumerator = new WaitForSecondsEnumerator(0.1f);
        var syncRunner  = new SyncRunner <Allocation0Enumerator>();
        var syncRunner2 = new SyncRunner <WaitForSecondsEnumerator>();
        var task        = TaskRunner.Instance.AllocateNewTaskRoutine(syncRunner2).SetEnumeratorRef(ref waitForSecondsEnumerator);
        var task2       = TaskRunner.Instance.AllocateNewTaskRoutine(syncRunner);
        var serialtask  = new SerialTaskCollection <Allocation0Enumerator>();

        int counter = 0;

        while (true)
        {
            yield return(task.Start());

            //yield return new Allocation0Enumerator(counter++); //nay this allocates
            yield return(task2.SetEnumerator(new Allocation0Enumerator(counter++)).Start()); //yay, this doesn't allocate!

            yield return(serialtask.Add(new Allocation0Enumerator(counter)));                //yay, this doesn't allocate!

            serialtask.Clear();

            yield return(null);
        }
    }
Example #3
0
 public Program2()
 {
     runner  = new SyncRunner <ExtraLeanSveltoTask <UltraSpecializedEnumerator> >(-1);
     runner2 = new SyncRunner <ExtraLeanSveltoTask <UItraSpecializedEnumeratorClass> >(-1);
     runner3 = new UpdateMonoRunner <ExtraLeanSveltoTask <UltraSpecializedEnumerator> >("test");
     runner4 = new UpdateMonoRunner <LeanSveltoTask <UltraSpecializedEnumerator> >("test2", 100000);
     runner5 = new UpdateMonoRunner <LeanSveltoTask <IEnumerator <TaskContract> > >("test3", 100000);
 }
Example #4
0
        //physicScheduler -> updateScheduler -> coroutineScheduler -> lateScheduler

        static StandardSchedulers()
        {
            syncScheduler        = new SyncRunner(false);
            multiThreadScheduler = new MultiThreadRunner("MultiThreadRunner", true);
#if UNITY_5 || UNITY_5_3_OR_NEWER
            coroutineScheduler = new CoroutineMonoRunner("StandardCoroutineRunner");
            physicScheduler    = new PhysicMonoRunner("StandardPhysicRunner");
            lateScheduler      = new LateMonoRunner("StandardLateRunner");
            updateScheduler    = new UpdateMonoRunner("StandardMonoRunner");
#endif
        }
    IEnumerator UpdateIT()
    {
        var waitForSecondsEnumerator = new WaitForSecondsEnumerator(0.1f);
        var syncRunner = new SyncRunner();

        while (true)
        {
            yield return(waitForSecondsEnumerator.RunOnSchedule(syncRunner));

            yield return(null);
        }
    }
        IEnumerator MainThreadOperations(ParticleCounter particleCounter)
        {
            var bounds = new Bounds(_BoundCenter, _BoundSize);

            var syncRunner = new SyncRunner();

            //these will help with synchronization between threads
            WaitForSignalEnumerator _waitForSignal      = new WaitForSignalEnumerator(() => _breakIt);
            WaitForSignalEnumerator _otherwaitForSignal = new WaitForSignalEnumerator();

            //Start the operations on other threads
            OperationsRunningOnOtherThreads(_waitForSignal, _otherwaitForSignal)
            .ThreadSafeRunOnSchedule(StandardSchedulers.multiThreadScheduler);

            //start the mainloop
            while (true)
            {
                _time = Time.time / 10;

                //wait until the other thread tell us that the data is ready to be used.
                //Note that I am stalling the main thread here! This is entirely up to you
                //if you don't want to stall it, as you can see with the other use cases
                _otherwaitForSignal.RunOnSchedule(syncRunner);
#if BENCHMARK
                if (PerformanceCheker.PerformanceProfiler.showingFPSValue > 30.0f)
                {
                    if (particleCounter.particlesLimit >= 16)
                    {
                        particleCounter.particlesLimit -= 16;
                    }

                    PerformanceCheker.PerformanceProfiler.particlesCount = particleCounter.particlesTransformed;
                }

                particleCounter.particlesTransformed = 0;
#endif

                _particleDataBuffer.SetData(_gpuparticleDataArr);

                //render the particles. I use DrawMeshInstancedIndirect but
                //there aren't any compute shaders running. This is so cool!
                Graphics.DrawMeshInstancedIndirect(_pointMesh, 0, _material,
                                                   bounds, _GPUInstancingArgsBuffer);

                //tell to the other thread that now it can perform the operations
                //for the next frame.
                _waitForSignal.Signal();

                //continue the cycle on the next frame
                yield return(null);
            }
        }
Example #7
0
        //yes this is running from another thread
        IEnumerator MainLoopOnOtherThread()
        {
            var syncRunner = new SyncRunner();

            var then = DateTime.Now;

            //Let's start the MainThread Loop
            RenderingOnCoroutineRunner().ThreadSafeRun();

            var CopyBufferOnUpdateRunner = new SimpleEnumerator(this); //let's avoid useless allocations

            //let's avoid allocations inside the loop
            Func <bool> onExternalBreak = OnExternalBreak;

            while (_breakIt == false)
            {
                _time = (float)(DateTime.Now - then).TotalSeconds;
                //Since we are using the SyncRunner, we don't need to yield the execution
                //as the SyncRunner is meant to stall the thread where it starts from.
                //The main thread will be stuck until the multiParallelTask has been
                //executed. A MultiParallelTaskCollection relies on its own
                //internal threads to run, so although the Main thread is stuck
                //the operation will complete
                _multiParallelTasks.ThreadSafeRunOnSchedule(syncRunner);
                //then it resumes here, however the just computed particles
                //cannot be passed to the compute buffer now,
                //as the Unity methods are not thread safe
                //so I have to run a simple enumerator on the main thread
                var continuator = CopyBufferOnUpdateRunner.ThreadSafeRunOnSchedule(StandardSchedulers.updateScheduler);
                //and I will wait it to complete, still exploting the continuation wrapper.
                //continuators can break on extra conditions too;
                continuator.BreakOnCondition(onExternalBreak);
                //We need to wait the MainThread to finish its operation before to run the
                //next iteration. So let's stall using the syncrunner;
                continuator.RunOnSchedule(syncRunner);
            }

            //the application is shutting down. This is not that necessary in a
            //standalone client, but necessary to stop the thread when the
            //application is stopped in the Editor to stop all the threads.
            _multiParallelTasks.ClearAndKill();

            TaskRunner.Instance.StopAndCleanupAllDefaultSchedulerTasks();

            yield break;
        }
Example #8
0
        IEnumerator WaitForNodesAdded()
        {
//Engines are usually designed to be able to cope with dynamic adding and removing of entities,
//but in this case I needed to know when the entities are ready to be processed. This wouldn't be
//strictly necessary if I coded the engine in a different way, but I decided to keep it simpler and more readable.
//That's why the engine starts immediately a task that waits for the nodes to be added(it assumes that all the
//entities are created on the same frame).This demo aims to be allocation free during the main execution, that's
//why all the tasks are prepared before hand. In this step, we prepare just one task that runs the main operations
//that must be executed on the entities.
            int count = 0;

#if FOURTH_TIER_EXAMPLE
            BoidNode[] _nodes;
#endif
            do
            {
#if FIRST_TIER_EXAMPLE || SECOND_TIER_EXAMPLE || THIRD_TIER_EXAMPLE
                count = _nodes.Count;
#endif
#if FOURTH_TIER_EXAMPLE
                _nodes = _structNodes.GetList(out count);
#endif
                yield return(null);
            } while (count == 0);

#if TURBO_EXAMPLE
            int numberOfThreads = (int)Mathf.Min(NUM_OF_THREADS, count);

            var countn = count / numberOfThreads;

            _multiParallelTask = new MultiThreadedParallelTaskCollection(numberOfThreads, false);
            _syncRunner        = new SyncRunner(true);

            for (int i = 0; i < numberOfThreads; i++)
            {
                _multiParallelTask.Add(new BoidEnumerator(_nodes, countn * i, countn));
            }
#elif FIRST_TIER_EXAMPLE || SECOND_TIER_EXAMPLE || THIRD_TIER_EXAMPLE || FOURTH_TIER_EXAMPLE
            _boidEnumerator = new BoidEnumerator(_nodes, 0, count);
#endif
            _testEnumerator = new TestEnumerator(_printNode);

            Update().ThreadSafeRunOnSchedule(StandardSchedulers.updateScheduler);
        }
        public void TestPooledTaskMemoryUsage()
        {
            var syncRunner = new SyncRunner <SlowTaskStruct>(2000);
            var task       = TaskRunner.Instance.AllocateNewTaskRoutine(syncRunner);

            task.SetEnumerator(new SlowTaskStruct(1));
            task.Start();

            Assert.That(() =>
            {
                task.SetEnumerator(new SlowTaskStruct(1));
            }, Is.Not.AllocatingGCMemory());
            Assert.That(() =>
                        { task.Start(); }, Is.Not.AllocatingGCMemory());
            Assert.That(() =>
                        { task.SetEnumerator(new SlowTaskStruct(1)); }, Is.Not.AllocatingGCMemory());
            Assert.That(() =>
                        { task.Start(); }, Is.Not.AllocatingGCMemory());
        }
Example #10
0
        public const uint NUM_OF_THREADS = 8; //must be divisible by 4 for this exercise as I am not handling reminders
#endif

        IEnumerator Update()
        {
            SyncRunner _syncRunner = new SyncRunner();

            while (true)
            {
#if TURBO_EXAMPLE
                //note: yielding here is meaningless as it runs on a sync scheduler
                //which will stop the execution of the thread until the operation is
                //done. I just didn't want to confuse you.
                //Note that while the task runs inside the Sync Runner, the
                //MultiThreadedParallelTaskCollection run on several threads
                //therefore main threads waits until the other threads are finished
                //I know it sounds complex, but look, is very simple right?
                yield return(_multiParallelTask.ThreadSafeRunOnSchedule(_syncRunner));

                //try this if you want to see what happens if you don't stall the mainthread
                //don't be shocked, while the demo will run at thousand of frame per second
                //the operation will call _testEnumerator still at the same frequency
                //it would have happened before. However this would be the way to use it
                //in a normal scenarion, as you don't want the main thread to be stalled.
                //yield return _multiParallelTask;
#else
                //yield on the sync scheduler as I want hold the main thread on purpose.
                //In real life you wouldn't use sync scheduler as you don't want to
                //stall the main thread.
                //note: RunOnSchedule (and ThreadSafeRunOnSchedule) allows to continue
                //the operation on another runner without stalling the current one.
                //yielding it allows the current operation to wait for the result.
                yield return(_boidEnumerator.RunOnScheduler(_syncRunner));
#endif
                //run the cached enumerator on the next coroutine phase, yield until it's done.
                //The thread will spin until is done. Yielding an enumerator on the same
                //runner actually executes it immediatly.
                yield return(_testEnumerator);

                //since _testEnumerator runs synchronously, we need to yield for a frame
                //at least once, otherwise this enumerator becomes totally synchronously
                //transforming into an infinite loop.
                //I understand this can look all obscure if you don't know how yield works :(
                yield return(null);
            }
        }
        IEnumerator OperationsRunningOnOtherThreads(WaitForSignalEnumerator waitForSignal,
                                                    WaitForSignalEnumerator otherWaitForSignal)
        {
            //a SyncRunner stop the execution of the thread until the task is not completed
            //the parameter true means that the runner will sleep in between yields
            var syncRunner = new SyncRunner();

            while (_breakIt == false)
            {
                //execute the tasks. The MultiParallelTask is a special collection
                //that uses N threads on its own to execute the tasks. This thread
                //doesn't need to do anything else meanwhile and will yield until
                //is done. That's why the syncrunner can sleep between yields, so
                //that this thread won't take much CPU just to wait the parallel
                //tasks to finish
                _multiParallelTasks.ThreadSafeRunOnSchedule(syncRunner);
#if BENCHMARK //do it x4
                _multiParallelTasks.ThreadSafeRunOnSchedule(syncRunner);
                _multiParallelTasks.ThreadSafeRunOnSchedule(syncRunner);
                _multiParallelTasks.ThreadSafeRunOnSchedule(syncRunner);
#endif
                //the 1 Million particles operation are done, let's signal that the
                //result can now be used
                otherWaitForSignal.Signal();
                //wait until the application is over or the main thread will tell
                //us that now we can perform again the particles operation. This
                //is an explicit while instead of a yield, just because if the _breakIt
                //condition, which is needed only because if this application runs
                //in the editor, the threads spawned will not stop until the Editor is
                //shut down.
                waitForSignal.RunOnSchedule(syncRunner);
            }

            //the application is shutting down. This is not that necessary in a
            //standalone client, but necessary to stop the thread when the
            //application is stopped in the Editor to stop all the threads.
            _multiParallelTasks.ClearAndKill();

            TaskRunner.Instance.StopAndCleanupAllDefaultSchedulerTasks();

            yield break;
        }