From d1c06e9c804bbaee2e098fcf4f5610679a9f12a9 Mon Sep 17 00:00:00 2001 From: Jeremy Ong Date: Tue, 3 Aug 2021 23:37:56 -0600 Subject: [PATCH] Add JobGraph::Reset, streamline execution, address feedback Also, came up with more useful benchmarks that actually measure the enqueue/dequeue operations for various simple workflows. For retained graphs, time-of-flight from submission to execution is ~1us per job, indicating job granularity should be >20us for retained jobs. For dynamic jobs, where we need to pay the cost of allocation, a granularity of ~100+ us may be advised. Signed-off-by: Jeremy Ong --- .../AzCore/Jobs/Internal/JobTypeEraser.h | 19 +- .../AzCore/AzCore/Jobs/JobExecutor.cpp | 87 ++--- .../AzCore/AzCore/Jobs/JobExecutor.h | 15 +- .../Framework/AzCore/AzCore/Jobs/JobGraph.cpp | 37 +- Code/Framework/AzCore/AzCore/Jobs/JobGraph.h | 20 +- .../Framework/AzCore/AzCore/Jobs/JobGraph.inl | 14 +- Code/Framework/AzCore/Tests/JobGraphTests.cpp | 362 +++--------------- 7 files changed, 168 insertions(+), 386 deletions(-) diff --git a/Code/Framework/AzCore/AzCore/Jobs/Internal/JobTypeEraser.h b/Code/Framework/AzCore/AzCore/Jobs/Internal/JobTypeEraser.h index 1455f1311a..11d7f955b4 100644 --- a/Code/Framework/AzCore/AzCore/Jobs/Internal/JobTypeEraser.h +++ b/Code/Framework/AzCore/AzCore/Jobs/Internal/JobTypeEraser.h @@ -8,6 +8,7 @@ #pragma once #include +#include #include #include #include @@ -105,15 +106,16 @@ namespace AZ::Internal class alignas(alignof(max_align_t)) TypeErasedJob final { public: - // The inline buffer allows the TypeErasedJob to span two cache lines. Lambdas can capture 56 - // bytes of data (7 pointers/references on a 64-bit machine) before spilling to the heap. - constexpr static size_t BufferSize = 128 - sizeof(size_t) * 6 - sizeof(uint32_t) - sizeof(JobDescriptor); + // The inline buffer allows the TypeErasedJob to span two cache lines. Lambdas can capture 48 + // bytes of data (6 pointers/references on a 64-bit machine) before spilling to the heap. + constexpr static size_t BufferSize = + 128 - sizeof(size_t) * 6 - sizeof(uint32_t) - sizeof(JobDescriptor) - sizeof(AZStd::atomic); TypeErasedJob() = default; - template + template TypeErasedJob(JobDescriptor const& desc, Lambda&& lambda) noexcept - : m_descriptor{desc} + : m_descriptor{ desc } { JobTypeEraser eraser; m_invoker = eraser.ErasedInvoker(); @@ -147,9 +149,9 @@ namespace AZ::Internal // Indicates if this job is a root of the graph (with no dependencies) bool IsRoot(); - void AttachToJobGraph(CompiledJobGraph& graph) noexcept + void Init() noexcept { - m_graph = &graph; + m_dependencyCount = m_inboundLinkCount; } void Invoke() @@ -167,7 +169,7 @@ namespace AZ::Internal friend class JobWorker; // This relocation avoids branches needed if the lambda type is unknown - template + template void TypedRelocate(Lambda&& lambda, char* destination) { if constexpr (AZStd::is_trivially_move_constructible_v) @@ -195,6 +197,7 @@ namespace AZ::Internal // class to equal the alignment of the largest scalar type available on the system (generally // 16 bytes). char m_buffer[BufferSize]; + AZStd::atomic m_dependencyCount; // This value is an offset in a buffer that stores dependency tracking information. uint32_t m_successorOffset = 0; diff --git a/Code/Framework/AzCore/AzCore/Jobs/JobExecutor.cpp b/Code/Framework/AzCore/AzCore/Jobs/JobExecutor.cpp index 16e4983752..aa7f696942 100644 --- a/Code/Framework/AzCore/AzCore/Jobs/JobExecutor.cpp +++ b/Code/Framework/AzCore/AzCore/Jobs/JobExecutor.cpp @@ -29,19 +29,18 @@ namespace AZ AZStd::vector&& jobs, AZStd::unordered_map>& links, size_t linkCount, - bool retained) - : m_remaining{ jobs.size() } - , m_retained{ retained } + JobGraph* parent) + : m_parent{ parent } { m_jobs = AZStd::move(jobs); - m_dependencyCounts = reinterpret_cast*>(azcalloc(sizeof(AZStd::atomic) * m_jobs.size())); m_successors.resize(linkCount); - uint32_t* cursor = m_successors.data(); + TypeErasedJob** cursor = m_successors.data(); for (size_t i = 0; i != m_jobs.size(); ++i) { TypeErasedJob& job = m_jobs[i]; + job.m_graph = this; job.m_successorOffset = cursor - m_successors.data(); cursor += job.m_outboundLinkCount; @@ -49,54 +48,42 @@ namespace AZ for (uint32_t j = 0; j != job.m_outboundLinkCount; ++j) { - m_successors[static_cast(job.m_successorOffset) + j] = links[i][j]; - } - - if (job.m_inboundLinkCount > 0) - { - m_dependencyCounts[i].store(job.m_inboundLinkCount, AZStd::memory_order_release); + m_successors[static_cast(job.m_successorOffset) + j] = &m_jobs[links[i][j]]; } } // TODO: Check for dependency cycles } - CompiledJobGraph::~CompiledJobGraph() + uint32_t CompiledJobGraph::Release() { - if (m_dependencyCounts) - { - azfree(m_dependencyCounts); - } - } + uint32_t remaining = --m_remaining; - void CompiledJobGraph::Release() - { - if (--m_remaining == 0) + if (m_parent) { - if (m_retained) + if (remaining == 1) { - m_remaining = m_jobs.size(); - for (size_t i = 0; i != m_jobs.size(); ++i) - { - TypeErasedJob& job = m_jobs[i]; - if (job.m_inboundLinkCount > 0) - { - m_dependencyCounts[i].store(job.m_inboundLinkCount, AZStd::memory_order_release); - } - } + // Allow the parent graph to be submitted again + m_parent->m_submitted = false; } - + } + else if (remaining == 0) + { if (m_waitEvent) { - m_waitEvent->m_submitted = false; m_waitEvent->Signal(); } - if (!m_retained) - { - azdestroy(this); - } + azdestroy(this); + return remaining; } + + if (m_waitEvent && remaining == (m_parent ? 1 : 0)) + { + m_waitEvent->Signal(); + } + + return remaining; } struct QueueStatus @@ -124,7 +111,7 @@ namespace AZ JobQueue(const JobQueue&) = delete; JobQueue& operator=(const JobQueue&) = delete; - bool Enqueue(TypeErasedJob* job); + void Enqueue(TypeErasedJob* job); TypeErasedJob* TryDequeue(); private: @@ -132,7 +119,7 @@ namespace AZ TypeErasedJob* m_queues[PriorityLevelCount][MaxQueueSize] = {}; }; - bool JobQueue::Enqueue(TypeErasedJob* job) + void JobQueue::Enqueue(TypeErasedJob* job) { uint8_t priority = job->GetPriorityNumber(); QueueStatus& status = m_status[priority]; @@ -159,7 +146,7 @@ namespace AZ expectedReserve = reserve; } - return status.head == status.tail - 1; + return; } // We failed to reserve a slot, try again @@ -233,9 +220,11 @@ namespace AZ void Enqueue(TypeErasedJob* job) { - if (m_queue.Enqueue(job)) + m_queue.Enqueue(job); + + if (!m_busy.exchange(true)) { - // The queue was empty prior to enqueueing the job, release the semaphore + // The worker was idle prior to enqueueing the job, release the semaphore m_semaphore.release(); } } @@ -245,14 +234,16 @@ namespace AZ { while (m_active) { + m_busy = false; m_semaphore.acquire(); - // m_semaphore.try_acquire_for(AZStd::chrono::microseconds{ 10 }); if (!m_active) { return; } + m_busy = true; + TypeErasedJob* job = m_queue.TryDequeue(); while (job) { @@ -260,10 +251,10 @@ namespace AZ // Decrement counts for all job successors for (size_t j = 0; j != job->m_outboundLinkCount; ++j) { - uint32_t successorIndex = job->m_graph->m_successors[job->m_successorOffset + j]; - if (--job->m_graph->m_dependencyCounts[successorIndex] == 0) + TypeErasedJob* successor = job->m_graph->m_successors[job->m_successorOffset + j]; + if (--successor->m_dependencyCount == 0) { - m_executor->Submit(job->m_graph->m_jobs[successorIndex]); + m_executor->Submit(*successor); } } @@ -277,6 +268,7 @@ namespace AZ AZStd::thread m_thread; AZStd::atomic m_active; + AZStd::atomic m_busy; AZStd::binary_semaphore m_semaphore; ::AZ::JobExecutor* m_executor; @@ -327,11 +319,6 @@ namespace AZ void JobExecutor::Submit(Internal::CompiledJobGraph& graph) { - for (Internal::TypeErasedJob& job : graph.Jobs()) - { - job.AttachToJobGraph(graph); - } - // Submit all jobs that have no inbound edges for (Internal::TypeErasedJob& job : graph.Jobs()) { diff --git a/Code/Framework/AzCore/AzCore/Jobs/JobExecutor.h b/Code/Framework/AzCore/AzCore/Jobs/JobExecutor.h index 9418126678..60176f0717 100644 --- a/Code/Framework/AzCore/AzCore/Jobs/JobExecutor.h +++ b/Code/Framework/AzCore/AzCore/Jobs/JobExecutor.h @@ -18,6 +18,7 @@ namespace AZ { class JobGraphEvent; + class JobGraph; namespace Internal { @@ -30,9 +31,7 @@ namespace AZ AZStd::vector&& jobs, AZStd::unordered_map>& links, size_t linkCount, - bool retained); - - ~CompiledJobGraph(); + JobGraph* parent); AZStd::vector& Jobs() noexcept { @@ -40,19 +39,19 @@ namespace AZ } // Indicate that a constituent job has finished and decrement a counter to determine if the - // graph should be freed - void Release(); + // graph should be freed (returns the value after atomic decrement) + uint32_t Release(); private: friend class JobGraph; friend class JobWorker; AZStd::vector m_jobs; - AZStd::vector m_successors; - AZStd::atomic* m_dependencyCounts = nullptr; + AZStd::vector m_successors; JobGraphEvent* m_waitEvent = nullptr; + // The pointer to the parent graph is set only if it is retained + JobGraph* m_parent = nullptr; AZStd::atomic m_remaining; - bool m_retained; }; class JobWorker; diff --git a/Code/Framework/AzCore/AzCore/Jobs/JobGraph.cpp b/Code/Framework/AzCore/AzCore/Jobs/JobGraph.cpp index b715e0ada7..6b34f1273a 100644 --- a/Code/Framework/AzCore/AzCore/Jobs/JobGraph.cpp +++ b/Code/Framework/AzCore/AzCore/Jobs/JobGraph.cpp @@ -30,10 +30,27 @@ namespace AZ { if (m_retained && m_compiledJobGraph) { - azdestroy(m_compiledJobGraph); + // This job graph has already finished and we are potentially responsible for its destruction + if (m_compiledJobGraph->Release() == 0) + { + azdestroy(m_compiledJobGraph); + } } } + void JobGraph::Reset() + { + AZ_Assert(!m_submitted, "Cannot reset a job graph while it is in flight"); + if (m_compiledJobGraph) + { + azdestroy(m_compiledJobGraph); + m_compiledJobGraph = nullptr; + } + m_jobs.clear(); + m_links.clear(); + m_linkCount = 0; + } + void JobGraph::Submit(JobGraphEvent* waitEvent) { SubmitOnExecutor(JobExecutor::Instance(), waitEvent); @@ -41,20 +58,28 @@ namespace AZ void JobGraph::SubmitOnExecutor(JobExecutor& executor, JobGraphEvent* waitEvent) { - m_submitted = true; - if (!m_compiledJobGraph) { - m_compiledJobGraph = aznew CompiledJobGraph(AZStd::move(m_jobs), m_links, m_linkCount, m_retained); + m_compiledJobGraph = aznew CompiledJobGraph(AZStd::move(m_jobs), m_links, m_linkCount, m_retained ? this : nullptr); } m_compiledJobGraph->m_waitEvent = waitEvent; + m_compiledJobGraph->m_remaining = m_compiledJobGraph->m_jobs.size() + (m_retained ? 1 : 0); + for (size_t i = 0; i != m_compiledJobGraph->m_jobs.size(); ++i) + { + m_compiledJobGraph->m_jobs[i].Init(); + } executor.Submit(*m_compiledJobGraph); - if (waitEvent) + if (m_retained) { - waitEvent->m_submitted = true; + m_submitted = true; + } + else + { + m_compiledJobGraph = nullptr; + Reset(); } } } diff --git a/Code/Framework/AzCore/AzCore/Jobs/JobGraph.h b/Code/Framework/AzCore/AzCore/Jobs/JobGraph.h index 62565ea78c..070236b0b5 100644 --- a/Code/Framework/AzCore/AzCore/Jobs/JobGraph.h +++ b/Code/Framework/AzCore/AzCore/Jobs/JobGraph.h @@ -12,7 +12,7 @@ // suited in the private CompiledJobGraph implementation instead to keep this header lean. #include #include -#include +#include #include #include #include @@ -30,10 +30,14 @@ namespace AZ class JobToken final { public: - // Indicate that this job must finish before the job passed as the argument + // Indicate that this job must finish before the job token(s) passed as the argument template void Precedes(JT&... tokens); + // Indicate that this job must finish after the job token(s) passed as the argument + template + void Succeeds(JT&... tokens); + private: friend class JobGraph; @@ -67,7 +71,6 @@ namespace AZ void Signal(); AZStd::binary_semaphore m_semaphore; - bool m_submitted = false; }; // The JobGraph encapsulates a set of jobs and their interdependencies. After adding @@ -81,13 +84,18 @@ namespace AZ public: ~JobGraph(); + // Reset the state of the job graph to begin recording jobs and edges again + // NOTE: Graph must be in a "settled" state (cannot be in-flight) + void Reset(); + // Add a job to the graph, retrieiving a token that can be used to express dependencies // between jobs. The first argument specifies the JobKind, used for tracking the job. + // NOTE: This operation is invalid if the graph is in-flight template JobToken AddJob(JobDescriptor const& descriptor, Lambda&& lambda); template - AZStd::fixed_vector AddJobs(JobDescriptor const& descriptor, Lambdas&&... lambdas); + AZStd::array AddJobs(JobDescriptor const& descriptor, Lambdas&&... lambdas); // By default, you are responsible for retaining the JobGraph, indicating you promise that // this JobGraph will live as long as it takes for all constituent jobs to complete. @@ -103,6 +111,7 @@ namespace AZ // of the job graph is expected to rely on either indirection, or safe overwriting // of previously used memory to supply new data (this can even be done as the first // job in the graph). + // NOTE: This operation is invalid if the graph is in-flight void Detach(); // Invoke the job graph, asserting if there are dependency violations. Note that @@ -122,6 +131,7 @@ namespace AZ private: friend class JobToken; + friend class Internal::CompiledJobGraph; Internal::CompiledJobGraph* m_compiledJobGraph = nullptr; @@ -132,7 +142,7 @@ namespace AZ uint32_t m_linkCount = 0; bool m_retained = true; - bool m_submitted = false; + AZStd::atomic m_submitted = false; }; } // namespace AZ diff --git a/Code/Framework/AzCore/AzCore/Jobs/JobGraph.inl b/Code/Framework/AzCore/AzCore/Jobs/JobGraph.inl index f541d9923f..ad1fb3505c 100644 --- a/Code/Framework/AzCore/AzCore/Jobs/JobGraph.inl +++ b/Code/Framework/AzCore/AzCore/Jobs/JobGraph.inl @@ -22,15 +22,19 @@ namespace AZ (PrecedesInternal(tokens), ...); } + template + inline void JobToken::Succeeds(JT&... tokens) + { + (tokens.PrecedesInternal(*this), ...); + } + inline bool JobGraphEvent::IsSignaled() { - AZ_Assert(m_submitted, "Querying the status of a job graph event that was never submitted along with the jobgraph"); return m_semaphore.try_acquire_for(AZStd::chrono::milliseconds{ 0 }); } inline void JobGraphEvent::Wait() { - AZ_Assert(m_submitted, "Waiting on a job graph event that was never submitted along with the jobgraph"); m_semaphore.acquire(); } @@ -42,7 +46,7 @@ namespace AZ template inline JobToken JobGraph::AddJob(JobDescriptor const& desc, Lambda&& lambda) { - AZ_Assert(!m_submitted, "Cannot mutate a JobGraph that was previously submitted."); + AZ_Assert(!m_submitted, "Cannot mutate a JobGraph that was previously submitted or in flight."); m_jobs.emplace_back(desc, AZStd::forward(lambda)); @@ -50,9 +54,9 @@ namespace AZ } template - inline AZStd::fixed_vector AddJobs(JobDescriptor const& descriptor, Lambdas&&... lambdas) + inline AZStd::array JobGraph::AddJobs(JobDescriptor const& descriptor, Lambdas&&... lambdas) { - return { AddJob(descriptor, lambdas)... }; + return { AddJob(descriptor, AZStd::forward(lambdas))... }; } inline void JobGraph::Detach() diff --git a/Code/Framework/AzCore/Tests/JobGraphTests.cpp b/Code/Framework/AzCore/Tests/JobGraphTests.cpp index 175881b89a..b080e5b679 100644 --- a/Code/Framework/AzCore/Tests/JobGraphTests.cpp +++ b/Code/Framework/AzCore/Tests/JobGraphTests.cpp @@ -336,8 +336,7 @@ namespace UnitTest // d a.Precedes(b, c); - b.Precedes(d); - c.Precedes(d); + d.Succeeds(b, c); JobGraphEvent ev; graph.SubmitOnExecutor(*m_executor, &ev); @@ -487,8 +486,7 @@ namespace UnitTest a.Precedes(b, c); b.Precedes(d); c.Precedes(e, f); - e.Precedes(g); - f.Precedes(g); + g.Succeeds(e, f); g.Precedes(d); JobGraphEvent ev; @@ -511,338 +509,94 @@ namespace Benchmark class JobGraphBenchmarkFixture : public ::benchmark::Fixture { public: - static const int32_t LIGHT_WEIGHT_JOB_CALCULATE_PI_DEPTH = 1; - static const int32_t MEDIUM_WEIGHT_JOB_CALCULATE_PI_DEPTH = 1024; - static const int32_t HEAVY_WEIGHT_JOB_CALCULATE_PI_DEPTH = 1048576; - - static const int32_t SMALL_NUMBER_OF_JOBS = 10; - static const int32_t MEDIUM_NUMBER_OF_JOBS = 1024; - static const int32_t LARGE_NUMBER_OF_JOBS = 16384; - static AZStd::atomic s_numIncompleteJobs; - - int m_depth = 1; - JobGraph* graphs; - void SetUp(benchmark::State&) override { - s_numIncompleteJobs = 0; - - m_executor = aznew JobExecutor(0); - graphs = new JobGraph[4]; - - // Generate some random priorities - m_randomPriorities.resize(LARGE_NUMBER_OF_JOBS); - std::mt19937_64 randomPriorityGenerator(1); // Always use the same seed - std::uniform_int_distribution<> randomPriorityDistribution(0, static_cast(AZ::JobPriority::PRIORITY_COUNT)); - std::generate( - m_randomPriorities.begin(), m_randomPriorities.end(), - [&randomPriorityDistribution, &randomPriorityGenerator]() - { - return randomPriorityDistribution(randomPriorityGenerator); - }); - - // Generate some random depths - m_randomDepths.resize(LARGE_NUMBER_OF_JOBS); - std::mt19937_64 randomDepthGenerator(1); // Always use the same seed - std::uniform_int_distribution<> randomDepthDistribution( - LIGHT_WEIGHT_JOB_CALCULATE_PI_DEPTH, HEAVY_WEIGHT_JOB_CALCULATE_PI_DEPTH); - std::generate( - m_randomDepths.begin(), m_randomDepths.end(), - [&randomDepthDistribution, &randomDepthGenerator]() - { - return randomDepthDistribution(randomDepthGenerator); - }); - - for (size_t i = 0; i != 4; ++i) - { - graphs[i].AddJob( - descriptors[i], - [this] - { - benchmark::DoNotOptimize(CalculatePi(m_depth)); - --s_numIncompleteJobs; - }); - } + executor = new JobExecutor; + graph = new JobGraph; } void TearDown(benchmark::State&) override { - delete[] graphs; - azdestroy(m_executor); - m_randomDepths = {}; - m_randomPriorities = {}; + delete graph; + delete executor; } JobDescriptor descriptors[4] = { { "critical", "benchmark", JobPriority::CRITICAL }, { "high", "benchmark", JobPriority::HIGH }, - { "mediium", "benchmark", JobPriority::MEDIUM }, + { "medium", "benchmark", JobPriority::MEDIUM }, { "low", "benchmark", JobPriority::LOW } }; - static inline double CalculatePi(AZ::u32 depth) - { - double pi = 0.0; - for (AZ::u32 i = 0; i < depth; ++i) - { - const double numerator = static_cast(((i % 2) * 2) - 1); - const double denominator = static_cast((2 * i) - 1); - pi += numerator / denominator; - } - return (pi - 1.0) * 4; - } - - void RunCalculatePiJob(int32_t depth, int8_t priority) - { - m_depth = depth; - ++s_numIncompleteJobs; - - graphs[priority].SubmitOnExecutor(*m_executor); - } - - void RunMultipleCalculatePiJobsWithDefaultPriority(uint32_t numberOfJobs, int32_t depth) - { - for (size_t i = 0; i != numberOfJobs; ++i) - { - RunCalculatePiJob(depth, 2); - } - - while (s_numIncompleteJobs > 0) - { - } - } - - void RunMultipleCalculatePiJobsWithRandomPriority(uint32_t numberOfJobs, int32_t depth) - { - for (size_t i = 0; i != numberOfJobs; ++i) - { - RunCalculatePiJob(depth, m_randomPriorities[i]); - } - - while (s_numIncompleteJobs > 0) - { - } - } - - void RunMultipleCalculatePiJobsWithRandomDepthAndDefaultPriority(uint32_t numberOfJobs) - { - for (size_t i = 0; i != numberOfJobs; ++i) - { - RunCalculatePiJob(m_randomDepths[i], 0); - } - - while (s_numIncompleteJobs > 0) - { - } - } - - void RunMultipleCalculatePiJobsWithRandomDepthAndRandomPriority(uint32_t numberOfJobs) - { - for (size_t i = 0; i != numberOfJobs; ++i) - { - RunCalculatePiJob(m_randomDepths[i], m_randomPriorities[i]); - } - - while (s_numIncompleteJobs > 0) - { - } - } - - JobExecutor* m_executor; - AZStd::vector m_randomDepths; - AZStd::vector m_randomPriorities; + JobGraph* graph; + JobExecutor* executor; }; - AZStd::atomic JobGraphBenchmarkFixture::s_numIncompleteJobs = 0; - - BENCHMARK_F(JobGraphBenchmarkFixture, RunSmallNumberOfLightWeightJobsWithDefaultPriority)(benchmark::State& state) + BENCHMARK_F(JobGraphBenchmarkFixture, QueueToDequeue)(benchmark::State& state) { + graph->AddJob( + descriptors[2], + [] + { + }); for (auto _ : state) { - RunMultipleCalculatePiJobsWithDefaultPriority(SMALL_NUMBER_OF_JOBS, LIGHT_WEIGHT_JOB_CALCULATE_PI_DEPTH); + JobGraphEvent ev; + graph->SubmitOnExecutor(*executor, &ev); + ev.Wait(); } } - BENCHMARK_F(JobGraphBenchmarkFixture, RunMediumNumberOfLightWeightJobsWithDefaultPriority)(benchmark::State& state) + BENCHMARK_F(JobGraphBenchmarkFixture, OneAfterAnother)(benchmark::State& state) { + auto a = graph->AddJob( + descriptors[2], + [] + { + }); + auto b = graph->AddJob( + descriptors[2], + [] + { + }); + a.Precedes(b); + for (auto _ : state) { - RunMultipleCalculatePiJobsWithDefaultPriority(MEDIUM_NUMBER_OF_JOBS, LIGHT_WEIGHT_JOB_CALCULATE_PI_DEPTH); + JobGraphEvent ev; + graph->SubmitOnExecutor(*executor, &ev); + ev.Wait(); } + executor->Drain(); } - BENCHMARK_F(JobGraphBenchmarkFixture, RunLargeNumberOfLightWeightJobsWithDefaultPriority)(benchmark::State& state) + BENCHMARK_F(JobGraphBenchmarkFixture, FourToOneJoin)(benchmark::State& state) { - for (auto _ : state) - { - RunMultipleCalculatePiJobsWithDefaultPriority(LARGE_NUMBER_OF_JOBS, LIGHT_WEIGHT_JOB_CALCULATE_PI_DEPTH); - } - } + auto [a, b, c, d, e] = graph->AddJobs( + descriptors[2], + [] + { + }, + [] + { + }, + [] + { + }, + [] + { + }, + [] + { + }); - BENCHMARK_F(JobGraphBenchmarkFixture, RunSmallNumberOfMediumWeightJobsWithDefaultPriority)(benchmark::State& state) - { - for (auto _ : state) - { - RunMultipleCalculatePiJobsWithDefaultPriority(SMALL_NUMBER_OF_JOBS, MEDIUM_WEIGHT_JOB_CALCULATE_PI_DEPTH); - } - } + e.Succeeds(a, b, c, d); - BENCHMARK_F(JobGraphBenchmarkFixture, RunMediumNumberOfMediumWeightJobsWithDefaultPriority)(benchmark::State& state) - { for (auto _ : state) { - RunMultipleCalculatePiJobsWithDefaultPriority(MEDIUM_NUMBER_OF_JOBS, MEDIUM_WEIGHT_JOB_CALCULATE_PI_DEPTH); - } - } - - BENCHMARK_F(JobGraphBenchmarkFixture, RunLargeNumberOfMediumWeightJobsWithDefaultPriority)(benchmark::State& state) - { - for (auto _ : state) - { - RunMultipleCalculatePiJobsWithDefaultPriority(LARGE_NUMBER_OF_JOBS, MEDIUM_WEIGHT_JOB_CALCULATE_PI_DEPTH); - } - } - - BENCHMARK_F(JobGraphBenchmarkFixture, RunSmallNumberOfHeavyWeightJobsWithDefaultPriority)(benchmark::State& state) - { - for (auto _ : state) - { - RunMultipleCalculatePiJobsWithDefaultPriority(SMALL_NUMBER_OF_JOBS, HEAVY_WEIGHT_JOB_CALCULATE_PI_DEPTH); - } - } - - BENCHMARK_F(JobGraphBenchmarkFixture, RunMediumNumberOfHeavyWeightJobsWithDefaultPriority)(benchmark::State& state) - { - for (auto _ : state) - { - RunMultipleCalculatePiJobsWithDefaultPriority(MEDIUM_NUMBER_OF_JOBS, HEAVY_WEIGHT_JOB_CALCULATE_PI_DEPTH); - } - } - - BENCHMARK_F(JobGraphBenchmarkFixture, RunLargeNumberOfHeavyWeightJobsWithDefaultPriority)(benchmark::State& state) - { - for (auto _ : state) - { - RunMultipleCalculatePiJobsWithDefaultPriority(LARGE_NUMBER_OF_JOBS, HEAVY_WEIGHT_JOB_CALCULATE_PI_DEPTH); - } - } - - BENCHMARK_F(JobGraphBenchmarkFixture, RunSmallNumberOfRandomWeightJobsWithDefaultPriority)(benchmark::State& state) - { - for (auto _ : state) - { - RunMultipleCalculatePiJobsWithRandomDepthAndDefaultPriority(SMALL_NUMBER_OF_JOBS); - } - } - - BENCHMARK_F(JobGraphBenchmarkFixture, RunMediumNumberOfRandomWeightJobsWithDefaultPriority)(benchmark::State& state) - { - for (auto _ : state) - { - RunMultipleCalculatePiJobsWithRandomDepthAndDefaultPriority(MEDIUM_NUMBER_OF_JOBS); - } - } - - BENCHMARK_F(JobGraphBenchmarkFixture, RunLargeNumberOfRandomWeightJobsWithDefaultPriority)(benchmark::State& state) - { - for (auto _ : state) - { - RunMultipleCalculatePiJobsWithRandomDepthAndDefaultPriority(LARGE_NUMBER_OF_JOBS); - } - } - - BENCHMARK_F(JobGraphBenchmarkFixture, RunSmallNumberOfLightWeightJobsWithRandomPriorities)(benchmark::State& state) - { - for (auto _ : state) - { - RunMultipleCalculatePiJobsWithRandomPriority(SMALL_NUMBER_OF_JOBS, LIGHT_WEIGHT_JOB_CALCULATE_PI_DEPTH); - } - } - - BENCHMARK_F(JobGraphBenchmarkFixture, RunMediumNumberOfLightWeightJobsWithRandomPriorities)(benchmark::State& state) - { - for (auto _ : state) - { - RunMultipleCalculatePiJobsWithRandomPriority(MEDIUM_NUMBER_OF_JOBS, LIGHT_WEIGHT_JOB_CALCULATE_PI_DEPTH); - } - } - - BENCHMARK_F(JobGraphBenchmarkFixture, RunLargeNumberOfLightWeightJobsWithRandomPriorities)(benchmark::State& state) - { - for (auto _ : state) - { - RunMultipleCalculatePiJobsWithRandomPriority(LARGE_NUMBER_OF_JOBS, LIGHT_WEIGHT_JOB_CALCULATE_PI_DEPTH); - } - } - - BENCHMARK_F(JobGraphBenchmarkFixture, RunSmallNumberOfMediumWeightJobsWithRandomPriorities)(benchmark::State& state) - { - for (auto _ : state) - { - RunMultipleCalculatePiJobsWithRandomPriority(SMALL_NUMBER_OF_JOBS, MEDIUM_WEIGHT_JOB_CALCULATE_PI_DEPTH); - } - } - - BENCHMARK_F(JobGraphBenchmarkFixture, RunMediumNumberOfMediumWeightJobsWithRandomPriorities)(benchmark::State& state) - { - for (auto _ : state) - { - RunMultipleCalculatePiJobsWithRandomPriority(MEDIUM_NUMBER_OF_JOBS, MEDIUM_WEIGHT_JOB_CALCULATE_PI_DEPTH); - } - } - - BENCHMARK_F(JobGraphBenchmarkFixture, RunLargeNumberOfMediumWeightJobsWithRandomPriorities)(benchmark::State& state) - { - for (auto _ : state) - { - RunMultipleCalculatePiJobsWithRandomPriority(LARGE_NUMBER_OF_JOBS, MEDIUM_WEIGHT_JOB_CALCULATE_PI_DEPTH); - } - } - - BENCHMARK_F(JobGraphBenchmarkFixture, RunSmallNumberOfHeavyWeightJobsWithRandomPriorities)(benchmark::State& state) - { - for (auto _ : state) - { - RunMultipleCalculatePiJobsWithRandomPriority(SMALL_NUMBER_OF_JOBS, HEAVY_WEIGHT_JOB_CALCULATE_PI_DEPTH); - } - } - - BENCHMARK_F(JobGraphBenchmarkFixture, RunMediumNumberOfHeavyWeightJobsWithRandomPriorities)(benchmark::State& state) - { - for (auto _ : state) - { - RunMultipleCalculatePiJobsWithRandomPriority(MEDIUM_NUMBER_OF_JOBS, HEAVY_WEIGHT_JOB_CALCULATE_PI_DEPTH); - } - } - - BENCHMARK_F(JobGraphBenchmarkFixture, RunLargeNumberOfHeavyWeightJobsWithRandomPriorities)(benchmark::State& state) - { - for (auto _ : state) - { - RunMultipleCalculatePiJobsWithRandomPriority(LARGE_NUMBER_OF_JOBS, HEAVY_WEIGHT_JOB_CALCULATE_PI_DEPTH); - } - } - - BENCHMARK_F(JobGraphBenchmarkFixture, RunSmallNumberOfRandomWeightJobsWithRandomPriorities)(benchmark::State& state) - { - for (auto _ : state) - { - RunMultipleCalculatePiJobsWithRandomDepthAndRandomPriority(SMALL_NUMBER_OF_JOBS); - } - } - - BENCHMARK_F(JobGraphBenchmarkFixture, RunMediumNumberOfRandomWeightJobsWithRandomPriorities)(benchmark::State& state) - { - for (auto _ : state) - { - RunMultipleCalculatePiJobsWithRandomDepthAndRandomPriority(MEDIUM_NUMBER_OF_JOBS); - } - } - - BENCHMARK_F(JobGraphBenchmarkFixture, RunLargeNumberOfRandomWeightJobsWithRandomPriorities)(benchmark::State& state) - { - for (auto _ : state) - { - RunMultipleCalculatePiJobsWithRandomDepthAndRandomPriority(LARGE_NUMBER_OF_JOBS); + JobGraphEvent ev; + graph->SubmitOnExecutor(*executor, &ev); + ev.Wait(); } + executor->Drain(); } } // namespace Benchmark #endif