From a2acac87d5a9584de0ee13caac8a99f841ef9eab Mon Sep 17 00:00:00 2001 From: "Tikhomirova, Kseniya" Date: Fri, 7 Oct 2022 16:17:42 -0700 Subject: [PATCH 01/22] [SYCL] Make host task blocking and detach empty command. Part 1 Signed-off-by: Tikhomirova, Kseniya --- sycl/source/detail/event_impl.hpp | 5 + sycl/source/detail/scheduler/commands.cpp | 23 +- sycl/source/detail/scheduler/commands.hpp | 26 +- .../source/detail/scheduler/graph_builder.cpp | 52 +- .../detail/scheduler/graph_processor.cpp | 19 +- sycl/source/detail/scheduler/scheduler.cpp | 38 +- sycl/source/detail/scheduler/scheduler.hpp | 7 + .../scheduler/EnqueueWithDependsOnDeps.cpp | 576 ++++++------------ .../scheduler/LeafLimitDiffContexts.cpp | 7 +- .../scheduler/SchedulerTestUtils.hpp | 6 +- .../scheduler/StreamInitDependencyOnHost.cpp | 12 +- 11 files changed, 295 insertions(+), 476 deletions(-) diff --git a/sycl/source/detail/event_impl.hpp b/sycl/source/detail/event_impl.hpp index d33da055a4f8e..81a018da51a71 100644 --- a/sycl/source/detail/event_impl.hpp +++ b/sycl/source/detail/event_impl.hpp @@ -234,6 +234,11 @@ class event_impl { /// state. bool isInitialized() const noexcept { return MIsInitialized; } + /// Checks if this event is complete. + /// + /// \return true if this event is complete. + bool isComplete() const { return MState == HES_Complete; } + private: // When instrumentation is enabled emits trace event for event wait begin and // returns the telemetry event generated for the wait diff --git a/sycl/source/detail/scheduler/commands.cpp b/sycl/source/detail/scheduler/commands.cpp index 8f5f18a090aa8..5c27c8b363127 100644 --- a/sycl/source/detail/scheduler/commands.cpp +++ b/sycl/source/detail/scheduler/commands.cpp @@ -321,10 +321,6 @@ class DispatchHostTask { HostTask.MHostTask.reset(); - // unblock user empty command here - EmptyCommand *EmptyCmd = MThisCmd->MEmptyCmd; - assert(EmptyCmd && "No empty command found"); - // Completing command's event along with unblocking enqueue readiness of // empty command may lead to quick deallocation of MThisCmd by some cleanup // process. Thus we'll copy deps prior to completing of event and unblocking @@ -339,9 +335,12 @@ class DispatchHostTask { std::vector Deps = MThisCmd->MDeps; // update self-event status + const std::vector &CmdsToEnqueue = MThisCmd->getBlockedUsers(); + MThisCmd->MEvent->setComplete(); - EmptyCmd->MEnqueueStatus = EnqueueResultT::SyclEnqueueReady; + Scheduler::enqueueUnblockedCommands(MThisCmd->MEvent, CmdsToEnqueue, + ToCleanUp); for (const DepDesc &Dep : Deps) Scheduler::enqueueLeavesOfReqUnlocked(Dep.MDepRequirement, ToCleanUp); @@ -370,9 +369,8 @@ void Command::waitForEvents(QueueImplPtr Queue, // we will have two different contexts for the same CPU device: C1, C2. // Also we have default host queue. This queue is accessible via // Scheduler. Now, let's assume we have three different events: E1(C1), - // E2(C1), E3(C2). Also, we have an EmptyCommand which is to be executed - // on host queue. The command's MPreparedDepsEvents will contain all three - // events (E1, E2, E3). Now, if piEventsWait is called for all three + // E2(C1), E3(C2). The command's MPreparedDepsEvents will contain all + // three events (E1, E2, E3). Now, if piEventsWait is called for all three // events we'll experience failure with CL_INVALID_CONTEXT 'cause these // events refer to different contexts. std::map> @@ -607,8 +605,7 @@ Command *Command::processDepEvent(EventImplPtr DepEvent, const DepDesc &Dep, // 3. Some types of commands do not produce PI events after they are enqueued // (e.g. alloca). Note that we can't check the pi event to make that // distinction since the command might still be unenqueued at this point. - bool PiEventExpected = (!DepEvent->is_host() && DepEvent->isInitialized()) || - getType() == CommandType::HOST_TASK; + bool PiEventExpected = (!DepEvent->is_host() && DepEvent->isInitialized()); if (auto *DepCmd = static_cast(DepEvent->getCommand())) PiEventExpected &= DepCmd->producesPiEvent(); @@ -2590,6 +2587,12 @@ bool ExecCGCommand::supportsPostEnqueueCleanup() const { ->hasAuxiliaryResources())); } +void Command::removeBlockedUser(Command *User) { + auto it = std::find(MBlockedUsers.begin(), MBlockedUsers.end(), User); + if (it != MBlockedUsers.end()) + MBlockedUsers.erase(it); +} + } // namespace detail } // __SYCL_INLINE_VER_NAMESPACE(_V1) } // namespace sycl diff --git a/sycl/source/detail/scheduler/commands.hpp b/sycl/source/detail/scheduler/commands.hpp index 77afa4936bc0a..319ce6c099c99 100644 --- a/sycl/source/detail/scheduler/commands.hpp +++ b/sycl/source/detail/scheduler/commands.hpp @@ -142,8 +142,21 @@ class Command { return MEnqueueStatus == EnqueueResultT::SyclEnqueueSuccess; } + // Shows that command could not be enqueued, now it may be true for empty task + // only bool isEnqueueBlocked() const { - return MEnqueueStatus == EnqueueResultT::SyclEnqueueBlocked; + return MIsBlockable && MEnqueueStatus == EnqueueResultT::SyclEnqueueBlocked; + } + // Shows thst command could be enqueud, but is blocking enqueue of all + // commands depending on it. Regular usage - host task. + bool isBlocking() const { return isHostTask() && MEvent->isComplete(); } + + void addBlockedUser(Command *NewUser) { MBlockedUsers.push_back(NewUser); } + + void removeBlockedUser(Command *User); + + const std::vector &getBlockedUsers() const { + return MBlockedUsers; } const QueueImplPtr &getQueue() const { return MQueue; } @@ -255,6 +268,11 @@ class Command { friend class DispatchHostTask; + /// Contains list of commands that depends on the host command explicitly (by + /// depends_on). Not involved into cleanup process since it is one-way link + /// and not holds resources. + std::vector MBlockedUsers; + public: const std::vector &getPreparedHostDepsEvents() const { return MPreparedHostDepsEvents; @@ -564,12 +582,6 @@ class ExecCGCommand : public Command { detail::CG &getCG() const { return *MCommandGroup; } - // MEmptyCmd is only employed if this command refers to host-task. - // The mechanism of lookup for single EmptyCommand amongst users of - // host-task-representing command is unreliable. This unreliability roots in - // the cleanup process. - EmptyCommand *MEmptyCmd = nullptr; - bool producesPiEvent() const final; bool supportsPostEnqueueCleanup() const final; diff --git a/sycl/source/detail/scheduler/graph_builder.cpp b/sycl/source/detail/scheduler/graph_builder.cpp index e4a609566d4cc..29b26ee286a30 100644 --- a/sycl/source/detail/scheduler/graph_builder.cpp +++ b/sycl/source/detail/scheduler/graph_builder.cpp @@ -923,7 +923,6 @@ Scheduler::GraphBuilder::addCG(std::unique_ptr CommandGroup, std::vector &ToEnqueue) { std::vector &Reqs = CommandGroup->MRequirements; const std::vector &Events = CommandGroup->MEvents; - const CG::CGTYPE CGType = CommandGroup->getType(); auto NewCmd = std::make_unique(std::move(CommandGroup), Queue); if (!NewCmd) @@ -1019,11 +1018,6 @@ Scheduler::GraphBuilder::addCG(std::unique_ptr CommandGroup, ToEnqueue.push_back(ConnCmd); } - if (CGType == CG::CGTYPE::CodeplayHostTask) - NewCmd->MEmptyCmd = - addEmptyCmd(NewCmd.get(), NewCmd->getCG().MRequirements, Queue, - Command::BlockReason::HostTask, ToEnqueue); - if (MPrintOptionsArray[AfterAddCG]) printGraphAsDot("after_addCG"); @@ -1323,8 +1317,6 @@ Command *Scheduler::GraphBuilder::connectDepEvent( throw runtime_error("Out of host memory", PI_ERROR_OUT_OF_HOST_MEMORY); } - EmptyCommand *EmptyCmd = nullptr; - if (Dep.MDepRequirement) { // make ConnectCmd depend on requirement // Dismiss the result here as it's not a connection now, @@ -1333,27 +1325,13 @@ Command *Scheduler::GraphBuilder::connectDepEvent( assert(reinterpret_cast(DepEvent->getCommand()) == Dep.MDepCommand); // add user to Dep.MDepCommand is already performed beyond this if branch - - // ConnectCmd is added as dependency to Cmd - // We build the following structure Cmd->EmptyCmd/ConnectCmd->DepCmd - // No need to add ConnectCmd to leaves buffer since it is a dependency - // for command Cmd that will be added there - - std::vector ToEnqueue; - const std::vector Reqs(1, Dep.MDepRequirement); - EmptyCmd = addEmptyCmd(ConnectCmd, Reqs, - Scheduler::getInstance().getDefaultHostQueue(), - Command::BlockReason::HostTask, ToEnqueue, false); - assert(ToEnqueue.size() == 0); - - // Depend Cmd on empty command { - DepDesc CmdDep = Dep; - CmdDep.MDepCommand = EmptyCmd; + DepDesc DepOnConnect = Dep; + DepOnConnect.MDepCommand = ConnectCmd; // Dismiss the result here as it's not a connection now, - // 'cause EmptyCmd is host one - (void)Cmd->addDep(CmdDep, ToCleanUp); + // 'cause ConnectCmd is host one + std::ignore = Cmd->addDep(DepOnConnect, ToCleanUp); } } else { // It is required condition in another a path and addUser will be set in @@ -1361,29 +1339,13 @@ Command *Scheduler::GraphBuilder::connectDepEvent( if (Command *DepCmd = reinterpret_cast(DepEvent->getCommand())) DepCmd->addUser(ConnectCmd); - std::vector ToEnqueue; - EmptyCmd = addEmptyCmd( - ConnectCmd, {}, Scheduler::getInstance().getDefaultHostQueue(), - Command::BlockReason::HostTask, ToEnqueue); - assert(ToEnqueue.size() == 0); + std::ignore = ConnectCmd->addDep(DepEvent, ToCleanUp); - // There is no requirement thus, empty command will only depend on - // ConnectCmd via its event. - // Dismiss the result here as it's not a connection now, - // 'cause ConnectCmd is host one. - (void)EmptyCmd->addDep(ConnectCmd->getEvent(), ToCleanUp); - (void)ConnectCmd->addDep(DepEvent, ToCleanUp); + std::ignore = Cmd->addDep(ConnectCmd->getEvent(), ToCleanUp); - // Depend Cmd on empty command - // Dismiss the result here as it's not a connection now, - // 'cause EmptyCmd is host one - (void)Cmd->addDep(EmptyCmd->getEvent(), ToCleanUp); - // Added by addDep in another path - EmptyCmd->addUser(Cmd); + ConnectCmd->addUser(Cmd); } - ConnectCmd->MEmptyCmd = EmptyCmd; - return ConnectCmd; } diff --git a/sycl/source/detail/scheduler/graph_processor.cpp b/sycl/source/detail/scheduler/graph_processor.cpp index 900fa713d58ce..dac25c706c0cb 100644 --- a/sycl/source/detail/scheduler/graph_processor.cpp +++ b/sycl/source/detail/scheduler/graph_processor.cpp @@ -32,7 +32,7 @@ void Scheduler::GraphProcessor::waitForEvent(EventImplPtr Event, return; EnqueueResultT Res; - bool Enqueued = enqueueCommand(Cmd, Res, ToCleanUp, BLOCKING); + bool Enqueued = enqueueCommand(Cmd, Res, ToCleanUp, Cmd, BLOCKING); if (!Enqueued && EnqueueResultT::SyclEnqueueFailed == Res.MResult) // TODO: Reschedule commands. throw runtime_error("Enqueue process failed.", PI_ERROR_INVALID_OPERATION); @@ -48,7 +48,8 @@ void Scheduler::GraphProcessor::waitForEvent(EventImplPtr Event, bool Scheduler::GraphProcessor::enqueueCommand( Command *Cmd, EnqueueResultT &EnqueueResult, - std::vector &ToCleanUp, BlockingT Blocking) { + std::vector &ToCleanUp, Command *RootCommand, + BlockingT Blocking) { if (!Cmd || Cmd->isSuccessfullyEnqueued()) return true; @@ -62,7 +63,8 @@ bool Scheduler::GraphProcessor::enqueueCommand( // first and exit immediately if any of the commands cannot be enqueued. for (const EventImplPtr &Event : Cmd->getPreparedDepsEvents()) { if (Command *DepCmd = static_cast(Event->getCommand())) - if (!enqueueCommand(DepCmd, EnqueueResult, ToCleanUp, Blocking)) + if (!enqueueCommand(DepCmd, EnqueueResult, ToCleanUp, RootCommand, + Blocking)) return false; } @@ -74,7 +76,8 @@ bool Scheduler::GraphProcessor::enqueueCommand( // completion stage and eliminate this event waiting in enqueue. for (const EventImplPtr &Event : Cmd->getPreparedHostDepsEvents()) { if (Command *DepCmd = static_cast(Event->getCommand())) - if (!enqueueCommand(DepCmd, EnqueueResult, ToCleanUp, Blocking)) + if (!enqueueCommand(DepCmd, EnqueueResult, ToCleanUp, RootCommand, + Blocking)) return false; } @@ -91,7 +94,13 @@ bool Scheduler::GraphProcessor::enqueueCommand( // on completion of C and starts cleanup process. This thread is still in the // middle of enqueue of B. The other thread modifies dependency list of A by // removing C out of it. Iterators become invalid. - return Cmd->enqueue(EnqueueResult, Blocking, ToCleanUp); + bool Result = Cmd->enqueue(EnqueueResult, Blocking, ToCleanUp); + if (Result && Cmd->isBlocking()) { + Cmd->addBlockedUser(RootCommand); + EnqueueResult = EnqueueResultT(EnqueueResultT::SyclEnqueueBlocked, Cmd); + return false; + } + return Result; } } // namespace detail diff --git a/sycl/source/detail/scheduler/scheduler.cpp b/sycl/source/detail/scheduler/scheduler.cpp index b43b53aa72dcf..be464a633608a 100644 --- a/sycl/source/detail/scheduler/scheduler.cpp +++ b/sycl/source/detail/scheduler/scheduler.cpp @@ -35,7 +35,7 @@ void Scheduler::waitForRecordToFinish(MemObjRecord *Record, std::vector ToCleanUp; for (Command *Cmd : Record->MReadLeaves) { EnqueueResultT Res; - bool Enqueued = GraphProcessor::enqueueCommand(Cmd, Res, ToCleanUp); + bool Enqueued = GraphProcessor::enqueueCommand(Cmd, Res, ToCleanUp, Cmd); if (!Enqueued && EnqueueResultT::SyclEnqueueFailed == Res.MResult) throw runtime_error("Enqueue process failed.", PI_ERROR_INVALID_OPERATION); @@ -47,7 +47,7 @@ void Scheduler::waitForRecordToFinish(MemObjRecord *Record, } for (Command *Cmd : Record->MWriteLeaves) { EnqueueResultT Res; - bool Enqueued = GraphProcessor::enqueueCommand(Cmd, Res, ToCleanUp); + bool Enqueued = GraphProcessor::enqueueCommand(Cmd, Res, ToCleanUp, Cmd); if (!Enqueued && EnqueueResultT::SyclEnqueueFailed == Res.MResult) throw runtime_error("Enqueue process failed.", PI_ERROR_INVALID_OPERATION); @@ -59,7 +59,8 @@ void Scheduler::waitForRecordToFinish(MemObjRecord *Record, for (AllocaCommandBase *AllocaCmd : Record->MAllocaCommands) { Command *ReleaseCmd = AllocaCmd->getReleaseCmd(); EnqueueResultT Res; - bool Enqueued = GraphProcessor::enqueueCommand(ReleaseCmd, Res, ToCleanUp); + bool Enqueued = + GraphProcessor::enqueueCommand(ReleaseCmd, Res, ToCleanUp, ReleaseCmd); if (!Enqueued && EnqueueResultT::SyclEnqueueFailed == Res.MResult) throw runtime_error("Enqueue process failed.", PI_ERROR_INVALID_OPERATION); @@ -130,7 +131,7 @@ EventImplPtr Scheduler::addCG(std::unique_ptr CommandGroup, }; for (Command *Cmd : AuxiliaryCmds) { - Enqueued = GraphProcessor::enqueueCommand(Cmd, Res, ToCleanUp); + Enqueued = GraphProcessor::enqueueCommand(Cmd, Res, ToCleanUp, Cmd); try { if (!Enqueued && EnqueueResultT::SyclEnqueueFailed == Res.MResult) throw runtime_error("Auxiliary enqueue process failed.", @@ -147,7 +148,8 @@ EventImplPtr Scheduler::addCG(std::unique_ptr CommandGroup, // TODO: Check if lazy mode. EnqueueResultT Res; try { - bool Enqueued = GraphProcessor::enqueueCommand(NewCmd, Res, ToCleanUp); + bool Enqueued = + GraphProcessor::enqueueCommand(NewCmd, Res, ToCleanUp, NewCmd); if (!Enqueued && EnqueueResultT::SyclEnqueueFailed == Res.MResult) throw runtime_error("Enqueue process failed.", PI_ERROR_INVALID_OPERATION); @@ -188,13 +190,13 @@ EventImplPtr Scheduler::addCopyBack(Requirement *Req) { bool Enqueued; for (Command *Cmd : AuxiliaryCmds) { - Enqueued = GraphProcessor::enqueueCommand(Cmd, Res, ToCleanUp); + Enqueued = GraphProcessor::enqueueCommand(Cmd, Res, ToCleanUp, Cmd); if (!Enqueued && EnqueueResultT::SyclEnqueueFailed == Res.MResult) throw runtime_error("Enqueue process failed.", PI_ERROR_INVALID_OPERATION); } - Enqueued = GraphProcessor::enqueueCommand(NewCmd, Res, ToCleanUp); + Enqueued = GraphProcessor::enqueueCommand(NewCmd, Res, ToCleanUp, NewCmd); if (!Enqueued && EnqueueResultT::SyclEnqueueFailed == Res.MResult) throw runtime_error("Enqueue process failed.", PI_ERROR_INVALID_OPERATION); @@ -319,14 +321,14 @@ EventImplPtr Scheduler::addHostAccessor(Requirement *Req) { bool Enqueued; for (Command *Cmd : AuxiliaryCmds) { - Enqueued = GraphProcessor::enqueueCommand(Cmd, Res, ToCleanUp); + Enqueued = GraphProcessor::enqueueCommand(Cmd, Res, ToCleanUp, Cmd); if (!Enqueued && EnqueueResultT::SyclEnqueueFailed == Res.MResult) throw runtime_error("Enqueue process failed.", PI_ERROR_INVALID_OPERATION); } if (Command *NewCmd = static_cast(NewCmdEvent->getCommand())) { - Enqueued = GraphProcessor::enqueueCommand(NewCmd, Res, ToCleanUp); + Enqueued = GraphProcessor::enqueueCommand(NewCmd, Res, ToCleanUp, NewCmd); if (!Enqueued && EnqueueResultT::SyclEnqueueFailed == Res.MResult) throw runtime_error("Enqueue process failed.", PI_ERROR_INVALID_OPERATION); @@ -359,7 +361,7 @@ void Scheduler::enqueueLeavesOfReqUnlocked(const Requirement *const Req, auto EnqueueLeaves = [&ToCleanUp](LeavesCollection &Leaves) { for (Command *Cmd : Leaves) { EnqueueResultT Res; - bool Enqueued = GraphProcessor::enqueueCommand(Cmd, Res, ToCleanUp); + bool Enqueued = GraphProcessor::enqueueCommand(Cmd, Res, ToCleanUp, Cmd); if (!Enqueued && EnqueueResultT::SyclEnqueueFailed == Res.MResult) throw runtime_error("Enqueue process failed.", PI_ERROR_INVALID_OPERATION); @@ -370,6 +372,19 @@ void Scheduler::enqueueLeavesOfReqUnlocked(const Requirement *const Req, EnqueueLeaves(Record->MWriteLeaves); } +void Scheduler::enqueueUnblockedCommands( + const EventImplPtr &UnblockedDep, const std::vector &ToEnqueue, + std::vector &ToCleanUp) { + for (auto &Command : ToEnqueue) { + EnqueueResultT Res; + bool Enqueued = + GraphProcessor::enqueueCommand(Command, Res, ToCleanUp, Command); + if (!Enqueued && EnqueueResultT::SyclEnqueueFailed == Res.MResult) + throw runtime_error("Enqueue process failed.", + PI_ERROR_INVALID_OPERATION); + } +} + void Scheduler::allocateStreamBuffers(stream_impl *Impl, size_t StreamBufferSize, size_t FlushBufferSize) { @@ -442,8 +457,7 @@ MemObjRecord *Scheduler::getMemObjRecord(const Requirement *const Req) { } void Scheduler::cleanupCommands(const std::vector &Cmds) { - if (Cmds.empty()) - { + if (Cmds.empty()) { std::lock_guard Lock{MDeferredCleanupMutex}; if (MDeferredCleanupCommands.empty()) return; diff --git a/sycl/source/detail/scheduler/scheduler.hpp b/sycl/source/detail/scheduler/scheduler.hpp index b6676fa4b0aa9..117241a4686cd 100644 --- a/sycl/source/detail/scheduler/scheduler.hpp +++ b/sycl/source/detail/scheduler/scheduler.hpp @@ -464,6 +464,11 @@ class Scheduler { static void enqueueLeavesOfReqUnlocked(const Requirement *const Req, std::vector &ToCleanUp); + static void + enqueueUnblockedCommands(const EventImplPtr &UnblockedDep, + const std::vector &CmdsToEnqueue, + std::vector &ToCleanUp); + /// Graph builder class. /// /// The graph builder provides means to change an existing graph (e.g. add @@ -752,6 +757,7 @@ class Scheduler { /// the lock is left in locked state. static bool enqueueCommand(Command *Cmd, EnqueueResultT &EnqueueResult, std::vector &ToCleanUp, + Command *RootCommand, BlockingT Blocking = NON_BLOCKING); }; @@ -777,6 +783,7 @@ class Scheduler { friend class DispatchHostTask; friend class queue_impl; friend class event_impl; + friend class ::MockScheduler; /// Stream buffers structure. /// diff --git a/sycl/unittests/scheduler/EnqueueWithDependsOnDeps.cpp b/sycl/unittests/scheduler/EnqueueWithDependsOnDeps.cpp index 95c598781cf73..b01e0e97e3b67 100644 --- a/sycl/unittests/scheduler/EnqueueWithDependsOnDeps.cpp +++ b/sycl/unittests/scheduler/EnqueueWithDependsOnDeps.cpp @@ -1,385 +1,191 @@ -//==------------ EnqueueWithDependsOnDeps.cpp --- Scheduler unit tests------==// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#include "SchedulerTest.hpp" -#include "SchedulerTestUtils.hpp" -#include - -#include -#include -#include - -#include - -using namespace sycl; -using EventImplPtr = std::shared_ptr; - -namespace DependsOnTest { -class MockHandlerCustom : public MockHandler { -public: - MockHandlerCustom(std::shared_ptr Queue, - bool IsHost) - : MockHandler(Queue, IsHost) {} - - std::unique_ptr finalize() { - std::unique_ptr CommandGroup; - switch (getType()) { - case sycl::detail::CG::Kernel: { - CommandGroup.reset(new sycl::detail::CGExecKernel( - getNDRDesc(), std::move(getHostKernel()), getKernel(), - std::move(MImpl->MKernelBundle), getArgsStorage(), getAccStorage(), - getSharedPtrStorage(), getRequirements(), getEvents(), getArgs(), - getKernelName(), getOSModuleHandle(), getStreamStorage(), - MImpl->MAuxiliaryResources, getCGType(), getCodeLoc())); - break; - } - case sycl::detail::CG::CodeplayHostTask: { - CommandGroup.reset(new detail::CGHostTask( - std::move(getHostTask()), getQueue(), getQueue()->getContextImplPtr(), - getArgs(), getArgsStorage(), getAccStorage(), getSharedPtrStorage(), - getRequirements(), getEvents(), getCGType(), getCodeLoc())); - break; - } - default: - throw sycl::runtime_error("Unhandled type of command group", - PI_ERROR_INVALID_OPERATION); - } - - return CommandGroup; - } -}; -} // namespace DependsOnTest -detail::Command *AddTaskCG(bool IsHost, MockScheduler &MS, - detail::QueueImplPtr DevQueue, - const std::vector &Events) { - std::vector ToEnqueue; - - // Emulating processing of command group function - DependsOnTest::MockHandlerCustom MockCGH(DevQueue, false); - - for (auto EventImpl : Events) - MockCGH.depends_on(detail::createSyclObjFromImpl(EventImpl)); - - if (IsHost) - MockCGH.host_task([] {}); - else { - kernel_bundle KernelBundle = - sycl::get_kernel_bundle( - DevQueue->get_context()); - auto ExecBundle = sycl::build(KernelBundle); - MockCGH.use_kernel_bundle(ExecBundle); - MockCGH.single_task>([] {}); - } - - std::unique_ptr CmdGroup = MockCGH.finalize(); - - detail::Command *NewCmd = - MS.addCG(std::move(CmdGroup), - IsHost ? MS.getDefaultHostQueue() : DevQueue, ToEnqueue); - EXPECT_EQ(ToEnqueue.size(), 0u); - return NewCmd; -} - -bool CheckTestExecutionRequirements(const platform &plt) { - if (plt.is_host()) { - std::cout << "Not run due to host-only environment\n"; - return false; - } - // This test only contains device image for SPIR-V capable devices. - if (plt.get_backend() != sycl::backend::opencl && - plt.get_backend() != sycl::backend::ext_oneapi_level_zero) { - std::cout << "Only OpenCL and Level Zero are supported for this test\n"; - return false; - } - return true; -} - -inline constexpr auto DisablePostEnqueueCleanupName = - "SYCL_DISABLE_POST_ENQUEUE_CLEANUP"; - -TEST_F(SchedulerTest, EnqueueNoMemObjTwoHostTasks) { - // Checks enqueue of two dependent host tasks - - unittest::ScopedEnvVar DisabledCleanup{ - DisablePostEnqueueCleanupName, "1", - detail::SYCLConfig::reset}; - - unittest::PiMock Mock; - platform Plt = Mock.getPlatform(); - if (!CheckTestExecutionRequirements(Plt)) - return; - - queue QueueDev(context(Plt), default_selector_v); - MockScheduler MS; - - detail::QueueImplPtr QueueDevImpl = detail::getSyclObjImpl(QueueDev); - detail::QueueImplPtr QueueHostImpl = MS.getDefaultHostQueue(); - - std::vector Events; - - detail::Command *Cmd1 = AddTaskCG(true, MS, QueueDevImpl, Events); - EventImplPtr Cmd1Event = Cmd1->getEvent(); - - // Simulate depends_on() call - Events.push_back(Cmd1Event); - detail::Command *Cmd2 = AddTaskCG(true, MS, QueueDevImpl, Events); - EventImplPtr Cmd2Event = Cmd2->getEvent(); - - detail::EnqueueResultT Result; - EXPECT_TRUE(MS.enqueueCommand(Cmd2, Result, detail::BlockingT::NON_BLOCKING)); - - // Preconditions for post enqueue checks - EXPECT_TRUE(Cmd1->isSuccessfullyEnqueued()); - EXPECT_TRUE(Cmd2->isSuccessfullyEnqueued()); - - Cmd2Event->wait(Cmd2Event); - EXPECT_EQ(Cmd1Event->get_info(), - info::event_command_status::complete); - EXPECT_EQ(Cmd2Event->get_info(), - info::event_command_status::complete); -} - -TEST_F(SchedulerTest, EnqueueNoMemObjKernelDepHost) { - // Checks enqueue of kernel depending on host task - unittest::ScopedEnvVar DisabledCleanup{ - DisablePostEnqueueCleanupName, "1", - detail::SYCLConfig::reset}; - - unittest::PiMock Mock; - platform Plt = Mock.getPlatform(); - if (!CheckTestExecutionRequirements(Plt)) - return; - - queue QueueDev(context(Plt), default_selector_v); - MockScheduler MS; - - detail::QueueImplPtr QueueDevImpl = detail::getSyclObjImpl(QueueDev); - - std::vector Events; - - detail::Command *Cmd1 = AddTaskCG(true, MS, QueueDevImpl, Events); - EventImplPtr Cmd1Event = Cmd1->getEvent(); - - // Simulate depends_on() call - Events.push_back(Cmd1Event); - detail::Command *Cmd2 = AddTaskCG(false, MS, QueueDevImpl, Events); - EventImplPtr Cmd2Event = Cmd2->getEvent(); - - detail::EnqueueResultT Result; - EXPECT_TRUE(MS.enqueueCommand(Cmd2, Result, detail::BlockingT::NON_BLOCKING)); - - // Preconditions for post enqueue checks - EXPECT_TRUE(Cmd1->isSuccessfullyEnqueued()); - EXPECT_TRUE(Cmd2->isSuccessfullyEnqueued()); - - Cmd2Event->wait(Cmd2Event); -} - -TEST_F(SchedulerTest, EnqueueNoMemObjHostDepKernel) { - // Checks enqueue of host task depending on kernel - unittest::ScopedEnvVar DisabledCleanup{ - DisablePostEnqueueCleanupName, "1", - detail::SYCLConfig::reset}; - - unittest::PiMock Mock; - platform Plt = Mock.getPlatform(); - if (!CheckTestExecutionRequirements(Plt)) - return; - - queue QueueDev(context(Plt), default_selector_v); - MockScheduler MS; - - detail::QueueImplPtr QueueDevImpl = detail::getSyclObjImpl(QueueDev); - - std::vector Events; - - detail::Command *Cmd1 = AddTaskCG(false, MS, QueueDevImpl, Events); - EventImplPtr Cmd1Event = Cmd1->getEvent(); - - // Simulate depends_on() call - Events.push_back(Cmd1Event); - detail::Command *Cmd2 = AddTaskCG(true, MS, QueueDevImpl, Events); - EventImplPtr Cmd2Event = Cmd2->getEvent(); - - detail::EnqueueResultT Result; - EXPECT_TRUE(MS.enqueueCommand(Cmd2, Result, detail::BlockingT::NON_BLOCKING)); - - // Preconditions for post enqueue checks - EXPECT_TRUE(Cmd1->isSuccessfullyEnqueued()); - EXPECT_TRUE(Cmd2->isSuccessfullyEnqueued()); - Cmd2Event->wait(Cmd2Event); -} - -TEST_F(SchedulerTest, EnqueueNoMemObjDoubleKernelDepHostBlocked) { - // Checks blocking command tranfer for dependent kernels and enqueue of root - // kernel on host task completion - unittest::ScopedEnvVar DisabledCleanup{ - DisablePostEnqueueCleanupName, "1", - detail::SYCLConfig::reset}; - - unittest::PiMock Mock; - platform Plt = Mock.getPlatform(); - if (!CheckTestExecutionRequirements(Plt)) - return; - - queue QueueDev(context(Plt), default_selector_v); - MockScheduler MS; - - detail::QueueImplPtr QueueDevImpl = detail::getSyclObjImpl(QueueDev); - - std::vector Events; - - detail::Command *Cmd1 = AddTaskCG(true, MS, QueueDevImpl, Events); - EventImplPtr Cmd1Event = Cmd1->getEvent(); - Cmd1->MEnqueueStatus = detail::EnqueueResultT::SyclEnqueueBlocked; - - // Depends on host task - Events.push_back(Cmd1Event); - detail::Command *Cmd2 = AddTaskCG(false, MS, QueueDevImpl, Events); - EventImplPtr Cmd2Event = Cmd2->getEvent(); - - // Depends on kernel depending on host task - Events.clear(); - Events.push_back(Cmd2Event); - detail::Command *Cmd3 = AddTaskCG(false, MS, QueueDevImpl, Events); - EventImplPtr Cmd3Event = Cmd2->getEvent(); - - detail::EnqueueResultT Result; - EXPECT_FALSE( - MS.enqueueCommand(Cmd2, Result, detail::BlockingT::NON_BLOCKING)); - EXPECT_EQ(Result.MResult, detail::EnqueueResultT::SyclEnqueueBlocked); - EXPECT_EQ(Result.MCmd, static_cast(Cmd1)); - EXPECT_FALSE( - MS.enqueueCommand(Cmd3, Result, detail::BlockingT::NON_BLOCKING)); - EXPECT_EQ(Result.MResult, detail::EnqueueResultT::SyclEnqueueBlocked); - EXPECT_EQ(Result.MCmd, static_cast(Cmd1)); - - // Preconditions for post enqueue checks - EXPECT_FALSE(Cmd1->isSuccessfullyEnqueued()); - EXPECT_FALSE(Cmd2->isSuccessfullyEnqueued()); - EXPECT_FALSE(Cmd3->isSuccessfullyEnqueued()); - - Cmd1->MEnqueueStatus = detail::EnqueueResultT::SyclEnqueueReady; - - EXPECT_TRUE(MS.enqueueCommand(Cmd3, Result, detail::BlockingT::NON_BLOCKING)); - - EXPECT_TRUE(Cmd1->isSuccessfullyEnqueued()); - EXPECT_TRUE(Cmd2->isSuccessfullyEnqueued()); - EXPECT_TRUE(Cmd3->isSuccessfullyEnqueued()); - - Cmd3Event->wait(Cmd2Event); -} - -std::vector> PassedNumEvents; -inline pi_result redefinedEventsWaitCustom(pi_uint32 num_events, - const pi_event *event_list) { - PassedNumEvents.push_back(std::make_pair(num_events, event_list)); - return PI_SUCCESS; -} - -std::vector> PassedNumEventsToLaunch; -inline pi_result redefinedEnqueueKernelLaunchCustom( - pi_queue, pi_kernel, pi_uint32, const size_t *, const size_t *, - const size_t *, pi_uint32 num_events, const pi_event *event_list, - pi_event *event) { - PassedNumEventsToLaunch.push_back(std::make_pair(num_events, event_list)); - *event = reinterpret_cast(new int{}); - return PI_SUCCESS; -} - -void EventsWaitVerification(queue &QueueDev) { - MockScheduler MS; - - detail::QueueImplPtr QueueDevImpl = detail::getSyclObjImpl(QueueDev); - - std::vector Events; - - detail::Command *Cmd1 = AddTaskCG(true, MS, QueueDevImpl, Events); - EventImplPtr Cmd1Event = Cmd1->getEvent(); - - // Depends on host task - Events.push_back(Cmd1Event); - detail::Command *Cmd2 = AddTaskCG(false, MS, QueueDevImpl, Events); - EventImplPtr Cmd2Event = Cmd2->getEvent(); - - // Depends on kernel depending on host task - Events.clear(); - Events.push_back(Cmd2Event); - detail::Command *Cmd3 = AddTaskCG(false, MS, QueueDevImpl, Events); - EventImplPtr Cmd3Event = Cmd2->getEvent(); - - detail::EnqueueResultT Result; - EXPECT_TRUE(MS.enqueueCommand(Cmd3, Result, detail::BlockingT::NON_BLOCKING)); - Cmd3Event->wait(Cmd3Event); - - // One piEventsWait call: - // kernel2 waits for kernel 1 by sending event list to enqueue launch call - // (depending on queue property). Cmd3Event.wait() waits for kernel2 via - // piEventsWait. - ASSERT_EQ(PassedNumEvents.size(), 1u); - auto [EventCount, EventArr] = PassedNumEvents[0]; - ASSERT_EQ(EventCount, 1u); - EXPECT_EQ(*EventArr, Cmd3Event->getHandleRef()); -} - -TEST_F(SchedulerTest, InOrderEnqueueNoMemObjDoubleKernelDepHost) { - // Checks blocking command tranfer for dependent kernels and enqueue of root - // kernel on host task completion - unittest::ScopedEnvVar DisabledCleanup{ - DisablePostEnqueueCleanupName, "1", - detail::SYCLConfig::reset}; - - unittest::PiMock Mock; - platform Plt = Mock.getPlatform(); - if (!CheckTestExecutionRequirements(Plt)) - return; - - Mock.redefine(redefinedEventsWaitCustom); - Mock.redefine( - redefinedEnqueueKernelLaunchCustom); - - { - queue QueueDev(context(Plt), default_selector_v); - PassedNumEvents.clear(); - PassedNumEventsToLaunch.clear(); - EventsWaitVerification(QueueDev); - // 1st -> kernel after host, no pi events - // 2nd -> kernel after kernel, 1 pi event - ASSERT_EQ(PassedNumEventsToLaunch.size(), 2u); - { - auto [EventCount, EventArr] = PassedNumEventsToLaunch[0]; - EXPECT_EQ(EventCount, 0u); - EXPECT_EQ(EventArr, nullptr); - } - { - auto [EventCount, EventArr] = PassedNumEventsToLaunch[1]; - EXPECT_EQ(EventCount, 1u); - } - } - - { - queue QueueDev(context(Plt), default_selector_v, - property::queue::in_order()); - PassedNumEvents.clear(); - PassedNumEventsToLaunch.clear(); - EventsWaitVerification(QueueDev); - // 1st -> kernel after host, no pi events - // 2nd -> kernel after kernel and in order queue, 0 pi event - ASSERT_EQ(PassedNumEventsToLaunch.size(), 2u); - { - auto [EventCount, EventArr] = PassedNumEventsToLaunch[0]; - EXPECT_EQ(EventCount, 0u); - EXPECT_EQ(EventArr, nullptr); - } - { - auto [EventCount, EventArr] = PassedNumEventsToLaunch[1]; - EXPECT_EQ(EventCount, 0u); - EXPECT_EQ(EventArr, nullptr); - } - } -} \ No newline at end of file +// //==------------ EnqueueWithDependsOnDeps.cpp --- Scheduler unit +// tests------==// +// // +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM +// Exceptions. +// // See https://llvm.org/LICENSE.txt for license information. +// // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// // +// //===----------------------------------------------------------------------===// + +// #include "SchedulerTest.hpp" +// #include "SchedulerTestUtils.hpp" +// #include + +// #include +// #include +// #include + +// #include + +// using namespace sycl; +// using EventImplPtr = std::shared_ptr; + +// namespace DependsOnTest { +// class MockHandlerCustom : public MockHandler { +// public: +// MockHandlerCustom(std::shared_ptr Queue, +// bool IsHost) +// : MockHandler(Queue, IsHost) {} + +// std::unique_ptr finalize() { +// std::unique_ptr CommandGroup; +// switch (getType()) { +// case sycl::detail::CG::Kernel: { +// CommandGroup.reset(new sycl::detail::CGExecKernel( +// getNDRDesc(), std::move(getHostKernel()), getKernel(), +// std::move(MImpl->MKernelBundle), getArgsStorage(), getAccStorage(), +// getSharedPtrStorage(), getRequirements(), getEvents(), getArgs(), +// getKernelName(), getOSModuleHandle(), getStreamStorage(), +// MImpl->MAuxiliaryResources, getCGType(), getCodeLoc())); +// break; +// } +// case sycl::detail::CG::CodeplayHostTask: { +// CommandGroup.reset(new detail::CGHostTask( +// std::move(getHostTask()), getQueue(), +// getQueue()->getContextImplPtr(), getArgs(), getArgsStorage(), +// getAccStorage(), getSharedPtrStorage(), getRequirements(), +// getEvents(), getCGType(), getCodeLoc())); +// break; +// } +// default: +// throw sycl::runtime_error("Unhandled type of command group", +// PI_ERROR_INVALID_OPERATION); +// } + +// return CommandGroup; +// } +// }; +// } // namespace DependsOnTest + +// enum TestCGType +// { +// KERNEL_TASK = 0x00, +// HOST_TASK = 0x01 +// }; + +// detail::Command *AddTaskCG(TestCGType Type, MockScheduler &MS, +// detail::QueueImplPtr DevQueue, +// const std::vector &Events) { +// std::vector ToEnqueue; + +// // Emulating processing of command group function +// DependsOnTest::MockHandlerCustom MockCGH(DevQueue, false); + +// for (auto EventImpl : Events) +// MockCGH.depends_on(detail::createSyclObjFromImpl(EventImpl)); + +// if (Type == TestCGType::HOST_TASK) +// MockCGH.host_task([] {}); +// else { +// kernel_bundle KernelBundle = +// sycl::get_kernel_bundle( +// DevQueue->get_context()); +// auto ExecBundle = sycl::build(KernelBundle); +// MockCGH.use_kernel_bundle(ExecBundle); +// MockCGH.single_task>([] {}); +// } + +// std::unique_ptr CmdGroup = MockCGH.finalize(); + +// detail::Command *NewCmd = +// MS.addCG(std::move(CmdGroup), +// Type == TestCGType::HOST_TASK ? MS.getDefaultHostQueue() : +// DevQueue, ToEnqueue); +// EXPECT_EQ(ToEnqueue.size(), 0u); +// return NewCmd; +// } + +// bool CheckTestExecutionRequirements(const platform &plt) { +// if (plt.is_host()) { +// std::cout << "Not run due to host-only environment\n"; +// return false; +// } +// // This test only contains device image for SPIR-V capable devices. +// if (plt.get_backend() != sycl::backend::opencl && +// plt.get_backend() != sycl::backend::ext_oneapi_level_zero) { +// std::cout << "Only OpenCL and Level Zero are supported for this test\n"; +// return false; +// } +// return true; +// } + +// void VerifyTaskStructureValidness( +// detail::Command *NewCmd, const std::vector +// &BlockingTasks) { +// ASSERT_NE(NewCmd, nullptr); +// EXPECT_EQ(NewCmd->getType(), detail::Command::RUN_CG); + +// EXPECT_TRUE(std::all_of( +// BlockingTasks.cbegin(), BlockingTasks.cend(), +// [&NewCmd](detail::Command* BlockingTask) { +// const auto &BlockedUsers = BlockingTask->getBlockedUsers(); +// return std::find(BlockedUsers.begin(), BlockedUsers.end(), NewCmd) != +// BlockedUsers.end(); +// })); +// } + +// inline constexpr auto DisablePostEnqueueCleanupName = +// "SYCL_DISABLE_POST_ENQUEUE_CLEANUP"; + +// TEST_F(SchedulerTest, EnqueueNoMemObjKernelDepHost) { +// // Checks enqueue of kernel depending on host task +// unittest::ScopedEnvVar DisabledCleanup{ +// DisablePostEnqueueCleanupName, "1", +// detail::SYCLConfig::reset}; + +// default_selector Selector; +// platform Plt{Selector}; + +// if (!CheckTestExecutionRequirements(Plt)) +// GTEST_SKIP(); + +// sycl::unittest::PiMock Mock; + +// queue QueueDev(context(Plt), Selector); +// MockScheduler MS; + +// detail::QueueImplPtr QueueDevImpl = detail::getSyclObjImpl(QueueDev); + +// std::vector Events; +// std::vector BlockingTasks; + +// detail::Command *Cmd1 = AddTaskCG(TestCGType::HOST_TASK, MS, QueueDevImpl, +// Events); EventImplPtr Cmd1Event = Cmd1->getEvent(); +// VerifyTaskStructureValidness(Cmd1, BlockingTasks); + +// // Simulate depends_on() call +// Events.push_back(Cmd1Event); +// BlockingTasks.push_back(Cmd1); +// detail::Command *Cmd2 = AddTaskCG(TestCGType::KERNEL_TASK, MS, +// QueueDevImpl, Events); EventImplPtr Cmd2Event = Cmd2->getEvent(); + +// detail::EnqueueResultT Result; +// EXPECT_FALSE(MS.enqueueCommand(Cmd2, Result, +// detail::BlockingT::NON_BLOCKING)); VerifyTaskStructureValidness(Cmd2, +// BlockingTasks); + +// // Preconditions for post enqueue checks +// EXPECT_TRUE(Cmd1->isSuccessfullyEnqueued()); +// EXPECT_FALSE(Cmd2->isSuccessfullyEnqueued()); + +// EXPECT_EQ(Cmd1Event->get_info(), +// info::event_command_status::submitted); +// EXPECT_EQ(Cmd2Event->get_info(), +// info::event_command_status::submitted); +// // Wait will cleanup Cmd1 - not able to use any more, but Cmd2 should not +// be +// // cleaned up yet since we disable post enqueue cleanup. +// Cmd1Event->wait(Cmd1Event); +// EXPECT_EQ(Cmd1Event->get_info(), +// info::event_command_status::complete); +// { +// // Write lock allows to wait till all actions on host task completion are +// // executed including blocked users enqueue +// auto Lock = MS.acquireOriginSchedGraphWriteLock(); +// Lock.lock(); +// EXPECT_TRUE(Cmd2->isSuccessfullyEnqueued()); +// } + +// Cmd2Event->wait(Cmd2Event); +// } \ No newline at end of file diff --git a/sycl/unittests/scheduler/LeafLimitDiffContexts.cpp b/sycl/unittests/scheduler/LeafLimitDiffContexts.cpp index 9700fa5fd6da4..727dca3f5e5be 100644 --- a/sycl/unittests/scheduler/LeafLimitDiffContexts.cpp +++ b/sycl/unittests/scheduler/LeafLimitDiffContexts.cpp @@ -28,7 +28,7 @@ inline constexpr auto DisablePostEnqueueCleanupName = // overflowed. // Checks that in case of different contexts for deleted leaf and a new one // ConnectCmd will be created and scheduler will build the following dependency -// structure: NewLeaf->EmptyCmd/ConnectCmd->OldLeaf +// structure: NewLeaf->ConnectCmd->OldLeaf TEST_F(SchedulerTest, LeafLimitDiffContexts) { // All of the mock commands are owned on the test side, prevent post enqueue // cleanup from deleting some of them. @@ -120,7 +120,7 @@ TEST_F(SchedulerTest, LeafLimitDiffContexts) { Leaves.end()); } - // Check NewLeaf->EmptyCmd/ConnectCmd->OldLeaf structure + // Check NewLeaf->ConnectCmd->OldLeaf structure MockCommand *OldestLeaf = AddedLeaves.front().get(); MockCommand *NewestLeaf = AddedLeaves.back().get(); // The only user for oldLeaf must be ConnectCmd @@ -136,10 +136,9 @@ TEST_F(SchedulerTest, LeafLimitDiffContexts) { // Check NewLeaf dependencies in depth by MUsers auto ConnectCmdIt = OldestLeaf->MUsers.begin(); ASSERT_EQ((*ConnectCmdIt)->MUsers.size(), 1U); - auto EmptyCmdIt = (*ConnectCmdIt)->MUsers.begin(); EXPECT_TRUE(std::any_of(NewestLeaf->MDeps.begin(), NewestLeaf->MDeps.end(), [&](const detail::DepDesc &DD) { - return DD.MDepCommand == (*EmptyCmdIt); + return DD.MDepCommand == (*ConnectCmdIt); })); // ConnectCmd is created internally in scheduler and not a mock object // This fact leads to active scheduler shutdown process that deletes a diff --git a/sycl/unittests/scheduler/SchedulerTestUtils.hpp b/sycl/unittests/scheduler/SchedulerTestUtils.hpp index 02511f92eca69..01c45cdcabad4 100644 --- a/sycl/unittests/scheduler/SchedulerTestUtils.hpp +++ b/sycl/unittests/scheduler/SchedulerTestUtils.hpp @@ -143,7 +143,7 @@ class MockScheduler : public sycl::detail::Scheduler { sycl::detail::EnqueueResultT &EnqueueResult, sycl::detail::BlockingT Blocking) { std::vector ToCleanUp; - return GraphProcessor::enqueueCommand(Cmd, EnqueueResult, ToCleanUp, + return GraphProcessor::enqueueCommand(Cmd, EnqueueResult, ToCleanUp, Cmd, Blocking); } @@ -156,6 +156,10 @@ class MockScheduler : public sycl::detail::Scheduler { } ReadLockT acquireGraphReadLock() { return ReadLockT{MGraphLock}; } + WriteLockT acquireOriginSchedGraphWriteLock() { + Scheduler &Sched = Scheduler::getInstance(); + return WriteLockT(Sched.MGraphLock, std::defer_lock); + } sycl::detail::Command * insertMemoryMove(sycl::detail::MemObjRecord *Record, diff --git a/sycl/unittests/scheduler/StreamInitDependencyOnHost.cpp b/sycl/unittests/scheduler/StreamInitDependencyOnHost.cpp index 6828d605ef87e..62e603fd6cf25 100644 --- a/sycl/unittests/scheduler/StreamInitDependencyOnHost.cpp +++ b/sycl/unittests/scheduler/StreamInitDependencyOnHost.cpp @@ -30,11 +30,10 @@ class MockHandlerStreamInit : public MockHandler { case detail::CG::RunOnHostIntel: { CommandGroup.reset(new detail::CGExecKernel( getNDRDesc(), std::move(getHostKernel()), getKernel(), - std::move(MImpl->MKernelBundle), - getArgsStorage(), getAccStorage(), getSharedPtrStorage(), - getRequirements(), getEvents(), getArgs(), getKernelName(), - getOSModuleHandle(), getStreamStorage(), std::move(MImpl->MAuxiliaryResources), - getCGType(), getCodeLoc())); + std::move(MImpl->MKernelBundle), getArgsStorage(), getAccStorage(), + getSharedPtrStorage(), getRequirements(), getEvents(), getArgs(), + getKernelName(), getOSModuleHandle(), getStreamStorage(), + std::move(MImpl->MAuxiliaryResources), getCGType(), getCodeLoc())); break; } default: @@ -128,8 +127,7 @@ TEST_F(SchedulerTest, StreamInitDependencyOnHost) { // Tree of dependencies should look like: // [MAIN_CG] -> [EMPTY_NODE {FlushBufMemObj}] -> [FILL_CG {FlushBufMemObj}] -> // [[ALLOC_TASK {FlushBufMemObj}] - std::vector DepCmdsTypes({CmdTypeTy::EMPTY_TASK, - CmdTypeTy::RUN_CG, // FILL_CG + std::vector DepCmdsTypes({CmdTypeTy::RUN_CG, // FILL_CG CmdTypeTy::ALLOCA}); ASSERT_TRUE(ValidateDepCommandsTree(NewCmd, DepCmdsTypes, FlushBufMemObjPtr)) << "Dependency on stream flush buffer initialization not found"; From 521a4cc7cd1940495fa2a07e2737dda08b623e2e Mon Sep 17 00:00:00 2001 From: "Tikhomirova, Kseniya" Date: Fri, 7 Oct 2022 16:23:27 -0700 Subject: [PATCH 02/22] Temporary disable unit test for depends_on Signed-off-by: Tikhomirova, Kseniya --- .../scheduler/EnqueueWithDependsOnDeps.cpp | 692 +++++++++--------- 1 file changed, 346 insertions(+), 346 deletions(-) diff --git a/sycl/unittests/scheduler/EnqueueWithDependsOnDeps.cpp b/sycl/unittests/scheduler/EnqueueWithDependsOnDeps.cpp index 29db4edc8c759..8b70cc0186bcc 100644 --- a/sycl/unittests/scheduler/EnqueueWithDependsOnDeps.cpp +++ b/sycl/unittests/scheduler/EnqueueWithDependsOnDeps.cpp @@ -1,349 +1,349 @@ -//==------------ EnqueueWithDependsOnDeps.cpp --- Scheduler unit tests------==// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#include "SchedulerTest.hpp" -#include "SchedulerTestUtils.hpp" - -#include -#include -#include - -#include - -using namespace sycl; -using EventImplPtr = std::shared_ptr; - -detail::Command *AddTaskCG(bool IsHost, MockScheduler &MS, - detail::QueueImplPtr DevQueue, - const std::vector &Events) { - std::vector ToEnqueue; - - // Emulating processing of command group function - MockHandlerCustomFinalize MockCGH(DevQueue, false); - - for (auto EventImpl : Events) - MockCGH.depends_on(detail::createSyclObjFromImpl(EventImpl)); - - if (IsHost) - MockCGH.host_task([] {}); - else { - kernel_bundle KernelBundle = - sycl::get_kernel_bundle( - DevQueue->get_context()); - auto ExecBundle = sycl::build(KernelBundle); - MockCGH.use_kernel_bundle(ExecBundle); - MockCGH.single_task>([] {}); - } - - std::unique_ptr CmdGroup = MockCGH.finalize(); - - detail::Command *NewCmd = - MS.addCG(std::move(CmdGroup), - IsHost ? MS.getDefaultHostQueue() : DevQueue, ToEnqueue); - EXPECT_EQ(ToEnqueue.size(), 0u); - return NewCmd; -} - -bool CheckTestExecutionRequirements(const platform &plt) { - if (plt.is_host()) { - std::cout << "Not run due to host-only environment\n"; - return false; - } - // This test only contains device image for SPIR-V capable devices. - if (plt.get_backend() != sycl::backend::opencl && - plt.get_backend() != sycl::backend::ext_oneapi_level_zero) { - std::cout << "Only OpenCL and Level Zero are supported for this test\n"; - return false; - } - return true; -} - -inline constexpr auto DisablePostEnqueueCleanupName = - "SYCL_DISABLE_POST_ENQUEUE_CLEANUP"; - -TEST_F(SchedulerTest, EnqueueNoMemObjTwoHostTasks) { - // Checks enqueue of two dependent host tasks - - unittest::ScopedEnvVar DisabledCleanup{ - DisablePostEnqueueCleanupName, "1", - detail::SYCLConfig::reset}; - - unittest::PiMock Mock; - platform Plt = Mock.getPlatform(); - if (!CheckTestExecutionRequirements(Plt)) - return; - - queue QueueDev(context(Plt), default_selector_v); - MockScheduler MS; - - detail::QueueImplPtr QueueDevImpl = detail::getSyclObjImpl(QueueDev); - detail::QueueImplPtr QueueHostImpl = MS.getDefaultHostQueue(); - - std::vector Events; - - detail::Command *Cmd1 = AddTaskCG(true, MS, QueueDevImpl, Events); - EventImplPtr Cmd1Event = Cmd1->getEvent(); - - // Simulate depends_on() call - Events.push_back(Cmd1Event); - detail::Command *Cmd2 = AddTaskCG(true, MS, QueueDevImpl, Events); - EventImplPtr Cmd2Event = Cmd2->getEvent(); - - detail::EnqueueResultT Result; - EXPECT_TRUE(MS.enqueueCommand(Cmd2, Result, detail::BlockingT::NON_BLOCKING)); - - // Preconditions for post enqueue checks - EXPECT_TRUE(Cmd1->isSuccessfullyEnqueued()); - EXPECT_TRUE(Cmd2->isSuccessfullyEnqueued()); - - Cmd2Event->wait(Cmd2Event); - EXPECT_EQ(Cmd1Event->get_info(), - info::event_command_status::complete); - EXPECT_EQ(Cmd2Event->get_info(), - info::event_command_status::complete); -} - -TEST_F(SchedulerTest, EnqueueNoMemObjKernelDepHost) { - // Checks enqueue of kernel depending on host task - unittest::ScopedEnvVar DisabledCleanup{ - DisablePostEnqueueCleanupName, "1", - detail::SYCLConfig::reset}; - - unittest::PiMock Mock; - platform Plt = Mock.getPlatform(); - if (!CheckTestExecutionRequirements(Plt)) - return; - - queue QueueDev(context(Plt), default_selector_v); - MockScheduler MS; - - detail::QueueImplPtr QueueDevImpl = detail::getSyclObjImpl(QueueDev); - - std::vector Events; - - detail::Command *Cmd1 = AddTaskCG(true, MS, QueueDevImpl, Events); - EventImplPtr Cmd1Event = Cmd1->getEvent(); - - // Simulate depends_on() call - Events.push_back(Cmd1Event); - detail::Command *Cmd2 = AddTaskCG(false, MS, QueueDevImpl, Events); - EventImplPtr Cmd2Event = Cmd2->getEvent(); - - detail::EnqueueResultT Result; - EXPECT_TRUE(MS.enqueueCommand(Cmd2, Result, detail::BlockingT::NON_BLOCKING)); - - // Preconditions for post enqueue checks - EXPECT_TRUE(Cmd1->isSuccessfullyEnqueued()); - EXPECT_TRUE(Cmd2->isSuccessfullyEnqueued()); - - Cmd2Event->wait(Cmd2Event); -} - -TEST_F(SchedulerTest, EnqueueNoMemObjHostDepKernel) { - // Checks enqueue of host task depending on kernel - unittest::ScopedEnvVar DisabledCleanup{ - DisablePostEnqueueCleanupName, "1", - detail::SYCLConfig::reset}; - - unittest::PiMock Mock; - platform Plt = Mock.getPlatform(); - if (!CheckTestExecutionRequirements(Plt)) - return; - - queue QueueDev(context(Plt), default_selector_v); - MockScheduler MS; - - detail::QueueImplPtr QueueDevImpl = detail::getSyclObjImpl(QueueDev); - - std::vector Events; +// //==------------ EnqueueWithDependsOnDeps.cpp --- Scheduler unit tests------==// +// // +// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// // See https://llvm.org/LICENSE.txt for license information. +// // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// // +// //===----------------------------------------------------------------------===// + +// #include "SchedulerTest.hpp" +// #include "SchedulerTestUtils.hpp" + +// #include +// #include +// #include + +// #include + +// using namespace sycl; +// using EventImplPtr = std::shared_ptr; + +// detail::Command *AddTaskCG(bool IsHost, MockScheduler &MS, +// detail::QueueImplPtr DevQueue, +// const std::vector &Events) { +// std::vector ToEnqueue; + +// // Emulating processing of command group function +// MockHandlerCustomFinalize MockCGH(DevQueue, false); + +// for (auto EventImpl : Events) +// MockCGH.depends_on(detail::createSyclObjFromImpl(EventImpl)); + +// if (IsHost) +// MockCGH.host_task([] {}); +// else { +// kernel_bundle KernelBundle = +// sycl::get_kernel_bundle( +// DevQueue->get_context()); +// auto ExecBundle = sycl::build(KernelBundle); +// MockCGH.use_kernel_bundle(ExecBundle); +// MockCGH.single_task>([] {}); +// } + +// std::unique_ptr CmdGroup = MockCGH.finalize(); + +// detail::Command *NewCmd = +// MS.addCG(std::move(CmdGroup), +// IsHost ? MS.getDefaultHostQueue() : DevQueue, ToEnqueue); +// EXPECT_EQ(ToEnqueue.size(), 0u); +// return NewCmd; +// } + +// bool CheckTestExecutionRequirements(const platform &plt) { +// if (plt.is_host()) { +// std::cout << "Not run due to host-only environment\n"; +// return false; +// } +// // This test only contains device image for SPIR-V capable devices. +// if (plt.get_backend() != sycl::backend::opencl && +// plt.get_backend() != sycl::backend::ext_oneapi_level_zero) { +// std::cout << "Only OpenCL and Level Zero are supported for this test\n"; +// return false; +// } +// return true; +// } + +// inline constexpr auto DisablePostEnqueueCleanupName = +// "SYCL_DISABLE_POST_ENQUEUE_CLEANUP"; + +// TEST_F(SchedulerTest, EnqueueNoMemObjTwoHostTasks) { +// // Checks enqueue of two dependent host tasks + +// unittest::ScopedEnvVar DisabledCleanup{ +// DisablePostEnqueueCleanupName, "1", +// detail::SYCLConfig::reset}; + +// unittest::PiMock Mock; +// platform Plt = Mock.getPlatform(); +// if (!CheckTestExecutionRequirements(Plt)) +// return; + +// queue QueueDev(context(Plt), default_selector_v); +// MockScheduler MS; + +// detail::QueueImplPtr QueueDevImpl = detail::getSyclObjImpl(QueueDev); +// detail::QueueImplPtr QueueHostImpl = MS.getDefaultHostQueue(); + +// std::vector Events; + +// detail::Command *Cmd1 = AddTaskCG(true, MS, QueueDevImpl, Events); +// EventImplPtr Cmd1Event = Cmd1->getEvent(); + +// // Simulate depends_on() call +// Events.push_back(Cmd1Event); +// detail::Command *Cmd2 = AddTaskCG(true, MS, QueueDevImpl, Events); +// EventImplPtr Cmd2Event = Cmd2->getEvent(); + +// detail::EnqueueResultT Result; +// EXPECT_TRUE(MS.enqueueCommand(Cmd2, Result, detail::BlockingT::NON_BLOCKING)); + +// // Preconditions for post enqueue checks +// EXPECT_TRUE(Cmd1->isSuccessfullyEnqueued()); +// EXPECT_TRUE(Cmd2->isSuccessfullyEnqueued()); + +// Cmd2Event->wait(Cmd2Event); +// EXPECT_EQ(Cmd1Event->get_info(), +// info::event_command_status::complete); +// EXPECT_EQ(Cmd2Event->get_info(), +// info::event_command_status::complete); +// } + +// TEST_F(SchedulerTest, EnqueueNoMemObjKernelDepHost) { +// // Checks enqueue of kernel depending on host task +// unittest::ScopedEnvVar DisabledCleanup{ +// DisablePostEnqueueCleanupName, "1", +// detail::SYCLConfig::reset}; + +// unittest::PiMock Mock; +// platform Plt = Mock.getPlatform(); +// if (!CheckTestExecutionRequirements(Plt)) +// return; + +// queue QueueDev(context(Plt), default_selector_v); +// MockScheduler MS; + +// detail::QueueImplPtr QueueDevImpl = detail::getSyclObjImpl(QueueDev); + +// std::vector Events; + +// detail::Command *Cmd1 = AddTaskCG(true, MS, QueueDevImpl, Events); +// EventImplPtr Cmd1Event = Cmd1->getEvent(); + +// // Simulate depends_on() call +// Events.push_back(Cmd1Event); +// detail::Command *Cmd2 = AddTaskCG(false, MS, QueueDevImpl, Events); +// EventImplPtr Cmd2Event = Cmd2->getEvent(); + +// detail::EnqueueResultT Result; +// EXPECT_TRUE(MS.enqueueCommand(Cmd2, Result, detail::BlockingT::NON_BLOCKING)); + +// // Preconditions for post enqueue checks +// EXPECT_TRUE(Cmd1->isSuccessfullyEnqueued()); +// EXPECT_TRUE(Cmd2->isSuccessfullyEnqueued()); + +// Cmd2Event->wait(Cmd2Event); +// } + +// TEST_F(SchedulerTest, EnqueueNoMemObjHostDepKernel) { +// // Checks enqueue of host task depending on kernel +// unittest::ScopedEnvVar DisabledCleanup{ +// DisablePostEnqueueCleanupName, "1", +// detail::SYCLConfig::reset}; + +// unittest::PiMock Mock; +// platform Plt = Mock.getPlatform(); +// if (!CheckTestExecutionRequirements(Plt)) +// return; + +// queue QueueDev(context(Plt), default_selector_v); +// MockScheduler MS; + +// detail::QueueImplPtr QueueDevImpl = detail::getSyclObjImpl(QueueDev); + +// std::vector Events; - detail::Command *Cmd1 = AddTaskCG(false, MS, QueueDevImpl, Events); - EventImplPtr Cmd1Event = Cmd1->getEvent(); +// detail::Command *Cmd1 = AddTaskCG(false, MS, QueueDevImpl, Events); +// EventImplPtr Cmd1Event = Cmd1->getEvent(); - // Simulate depends_on() call - Events.push_back(Cmd1Event); - detail::Command *Cmd2 = AddTaskCG(true, MS, QueueDevImpl, Events); - EventImplPtr Cmd2Event = Cmd2->getEvent(); - - detail::EnqueueResultT Result; - EXPECT_TRUE(MS.enqueueCommand(Cmd2, Result, detail::BlockingT::NON_BLOCKING)); +// // Simulate depends_on() call +// Events.push_back(Cmd1Event); +// detail::Command *Cmd2 = AddTaskCG(true, MS, QueueDevImpl, Events); +// EventImplPtr Cmd2Event = Cmd2->getEvent(); + +// detail::EnqueueResultT Result; +// EXPECT_TRUE(MS.enqueueCommand(Cmd2, Result, detail::BlockingT::NON_BLOCKING)); - // Preconditions for post enqueue checks - EXPECT_TRUE(Cmd1->isSuccessfullyEnqueued()); - EXPECT_TRUE(Cmd2->isSuccessfullyEnqueued()); - Cmd2Event->wait(Cmd2Event); -} - -TEST_F(SchedulerTest, EnqueueNoMemObjDoubleKernelDepHostBlocked) { - // Checks blocking command tranfer for dependent kernels and enqueue of root - // kernel on host task completion - unittest::ScopedEnvVar DisabledCleanup{ - DisablePostEnqueueCleanupName, "1", - detail::SYCLConfig::reset}; - - unittest::PiMock Mock; - platform Plt = Mock.getPlatform(); - if (!CheckTestExecutionRequirements(Plt)) - return; - - queue QueueDev(context(Plt), default_selector_v); - MockScheduler MS; - - detail::QueueImplPtr QueueDevImpl = detail::getSyclObjImpl(QueueDev); - - std::vector Events; - - detail::Command *Cmd1 = AddTaskCG(true, MS, QueueDevImpl, Events); - EventImplPtr Cmd1Event = Cmd1->getEvent(); - Cmd1->MEnqueueStatus = detail::EnqueueResultT::SyclEnqueueBlocked; - - // Depends on host task - Events.push_back(Cmd1Event); - detail::Command *Cmd2 = AddTaskCG(false, MS, QueueDevImpl, Events); - EventImplPtr Cmd2Event = Cmd2->getEvent(); - - // Depends on kernel depending on host task - Events.clear(); - Events.push_back(Cmd2Event); - detail::Command *Cmd3 = AddTaskCG(false, MS, QueueDevImpl, Events); - EventImplPtr Cmd3Event = Cmd2->getEvent(); - - detail::EnqueueResultT Result; - EXPECT_FALSE( - MS.enqueueCommand(Cmd2, Result, detail::BlockingT::NON_BLOCKING)); - EXPECT_EQ(Result.MResult, detail::EnqueueResultT::SyclEnqueueBlocked); - EXPECT_EQ(Result.MCmd, static_cast(Cmd1)); - EXPECT_FALSE( - MS.enqueueCommand(Cmd3, Result, detail::BlockingT::NON_BLOCKING)); - EXPECT_EQ(Result.MResult, detail::EnqueueResultT::SyclEnqueueBlocked); - EXPECT_EQ(Result.MCmd, static_cast(Cmd1)); - - // Preconditions for post enqueue checks - EXPECT_FALSE(Cmd1->isSuccessfullyEnqueued()); - EXPECT_FALSE(Cmd2->isSuccessfullyEnqueued()); - EXPECT_FALSE(Cmd3->isSuccessfullyEnqueued()); - - Cmd1->MEnqueueStatus = detail::EnqueueResultT::SyclEnqueueReady; - - EXPECT_TRUE(MS.enqueueCommand(Cmd3, Result, detail::BlockingT::NON_BLOCKING)); - - EXPECT_TRUE(Cmd1->isSuccessfullyEnqueued()); - EXPECT_TRUE(Cmd2->isSuccessfullyEnqueued()); - EXPECT_TRUE(Cmd3->isSuccessfullyEnqueued()); - - Cmd3Event->wait(Cmd2Event); -} - -std::vector> PassedNumEvents; -inline pi_result redefinedEventsWaitCustom(pi_uint32 num_events, - const pi_event *event_list) { - PassedNumEvents.push_back(std::make_pair(num_events, event_list)); - return PI_SUCCESS; -} - -std::vector> PassedNumEventsToLaunch; -inline pi_result redefinedEnqueueKernelLaunchCustom( - pi_queue, pi_kernel, pi_uint32, const size_t *, const size_t *, - const size_t *, pi_uint32 num_events, const pi_event *event_list, - pi_event *event) { - PassedNumEventsToLaunch.push_back(std::make_pair(num_events, event_list)); - *event = reinterpret_cast(new int{}); - return PI_SUCCESS; -} - -void EventsWaitVerification(queue &QueueDev) { - MockScheduler MS; - - detail::QueueImplPtr QueueDevImpl = detail::getSyclObjImpl(QueueDev); - - std::vector Events; - - detail::Command *Cmd1 = AddTaskCG(true, MS, QueueDevImpl, Events); - EventImplPtr Cmd1Event = Cmd1->getEvent(); - - // Depends on host task - Events.push_back(Cmd1Event); - detail::Command *Cmd2 = AddTaskCG(false, MS, QueueDevImpl, Events); - EventImplPtr Cmd2Event = Cmd2->getEvent(); - - // Depends on kernel depending on host task - Events.clear(); - Events.push_back(Cmd2Event); - detail::Command *Cmd3 = AddTaskCG(false, MS, QueueDevImpl, Events); - EventImplPtr Cmd3Event = Cmd2->getEvent(); - - detail::EnqueueResultT Result; - EXPECT_TRUE(MS.enqueueCommand(Cmd3, Result, detail::BlockingT::NON_BLOCKING)); - Cmd3Event->wait(Cmd3Event); - - // One piEventsWait call: - // kernel2 waits for kernel 1 by sending event list to enqueue launch call - // (depending on queue property). Cmd3Event.wait() waits for kernel2 via - // piEventsWait. - ASSERT_EQ(PassedNumEvents.size(), 1u); - auto [EventCount, EventArr] = PassedNumEvents[0]; - ASSERT_EQ(EventCount, 1u); - EXPECT_EQ(*EventArr, Cmd3Event->getHandleRef()); -} - -TEST_F(SchedulerTest, InOrderEnqueueNoMemObjDoubleKernelDepHost) { - // Checks blocking command tranfer for dependent kernels and enqueue of root - // kernel on host task completion - unittest::ScopedEnvVar DisabledCleanup{ - DisablePostEnqueueCleanupName, "1", - detail::SYCLConfig::reset}; - - unittest::PiMock Mock; - platform Plt = Mock.getPlatform(); - if (!CheckTestExecutionRequirements(Plt)) - return; - - Mock.redefine(redefinedEventsWaitCustom); - Mock.redefine( - redefinedEnqueueKernelLaunchCustom); - - { - queue QueueDev(context(Plt), default_selector_v); - PassedNumEvents.clear(); - PassedNumEventsToLaunch.clear(); - EventsWaitVerification(QueueDev); - // 1st -> kernel after host, no pi events - // 2nd -> kernel after kernel, 1 pi event - ASSERT_EQ(PassedNumEventsToLaunch.size(), 2u); - { - auto [EventCount, EventArr] = PassedNumEventsToLaunch[0]; - EXPECT_EQ(EventCount, 0u); - EXPECT_EQ(EventArr, nullptr); - } - { - auto [EventCount, EventArr] = PassedNumEventsToLaunch[1]; - EXPECT_EQ(EventCount, 1u); - } - } - - { - queue QueueDev(context(Plt), default_selector_v, - property::queue::in_order()); - PassedNumEvents.clear(); - PassedNumEventsToLaunch.clear(); - EventsWaitVerification(QueueDev); - // 1st -> kernel after host, no pi events - // 2nd -> kernel after kernel and in order queue, 0 pi event - ASSERT_EQ(PassedNumEventsToLaunch.size(), 2u); - { - auto [EventCount, EventArr] = PassedNumEventsToLaunch[0]; - EXPECT_EQ(EventCount, 0u); - EXPECT_EQ(EventArr, nullptr); - } - { - auto [EventCount, EventArr] = PassedNumEventsToLaunch[1]; - EXPECT_EQ(EventCount, 0u); - EXPECT_EQ(EventArr, nullptr); - } - } -} \ No newline at end of file +// // Preconditions for post enqueue checks +// EXPECT_TRUE(Cmd1->isSuccessfullyEnqueued()); +// EXPECT_TRUE(Cmd2->isSuccessfullyEnqueued()); +// Cmd2Event->wait(Cmd2Event); +// } + +// TEST_F(SchedulerTest, EnqueueNoMemObjDoubleKernelDepHostBlocked) { +// // Checks blocking command tranfer for dependent kernels and enqueue of root +// // kernel on host task completion +// unittest::ScopedEnvVar DisabledCleanup{ +// DisablePostEnqueueCleanupName, "1", +// detail::SYCLConfig::reset}; + +// unittest::PiMock Mock; +// platform Plt = Mock.getPlatform(); +// if (!CheckTestExecutionRequirements(Plt)) +// return; + +// queue QueueDev(context(Plt), default_selector_v); +// MockScheduler MS; + +// detail::QueueImplPtr QueueDevImpl = detail::getSyclObjImpl(QueueDev); + +// std::vector Events; + +// detail::Command *Cmd1 = AddTaskCG(true, MS, QueueDevImpl, Events); +// EventImplPtr Cmd1Event = Cmd1->getEvent(); +// Cmd1->MEnqueueStatus = detail::EnqueueResultT::SyclEnqueueBlocked; + +// // Depends on host task +// Events.push_back(Cmd1Event); +// detail::Command *Cmd2 = AddTaskCG(false, MS, QueueDevImpl, Events); +// EventImplPtr Cmd2Event = Cmd2->getEvent(); + +// // Depends on kernel depending on host task +// Events.clear(); +// Events.push_back(Cmd2Event); +// detail::Command *Cmd3 = AddTaskCG(false, MS, QueueDevImpl, Events); +// EventImplPtr Cmd3Event = Cmd2->getEvent(); + +// detail::EnqueueResultT Result; +// EXPECT_FALSE( +// MS.enqueueCommand(Cmd2, Result, detail::BlockingT::NON_BLOCKING)); +// EXPECT_EQ(Result.MResult, detail::EnqueueResultT::SyclEnqueueBlocked); +// EXPECT_EQ(Result.MCmd, static_cast(Cmd1)); +// EXPECT_FALSE( +// MS.enqueueCommand(Cmd3, Result, detail::BlockingT::NON_BLOCKING)); +// EXPECT_EQ(Result.MResult, detail::EnqueueResultT::SyclEnqueueBlocked); +// EXPECT_EQ(Result.MCmd, static_cast(Cmd1)); + +// // Preconditions for post enqueue checks +// EXPECT_FALSE(Cmd1->isSuccessfullyEnqueued()); +// EXPECT_FALSE(Cmd2->isSuccessfullyEnqueued()); +// EXPECT_FALSE(Cmd3->isSuccessfullyEnqueued()); + +// Cmd1->MEnqueueStatus = detail::EnqueueResultT::SyclEnqueueReady; + +// EXPECT_TRUE(MS.enqueueCommand(Cmd3, Result, detail::BlockingT::NON_BLOCKING)); + +// EXPECT_TRUE(Cmd1->isSuccessfullyEnqueued()); +// EXPECT_TRUE(Cmd2->isSuccessfullyEnqueued()); +// EXPECT_TRUE(Cmd3->isSuccessfullyEnqueued()); + +// Cmd3Event->wait(Cmd2Event); +// } + +// std::vector> PassedNumEvents; +// inline pi_result redefinedEventsWaitCustom(pi_uint32 num_events, +// const pi_event *event_list) { +// PassedNumEvents.push_back(std::make_pair(num_events, event_list)); +// return PI_SUCCESS; +// } + +// std::vector> PassedNumEventsToLaunch; +// inline pi_result redefinedEnqueueKernelLaunchCustom( +// pi_queue, pi_kernel, pi_uint32, const size_t *, const size_t *, +// const size_t *, pi_uint32 num_events, const pi_event *event_list, +// pi_event *event) { +// PassedNumEventsToLaunch.push_back(std::make_pair(num_events, event_list)); +// *event = reinterpret_cast(new int{}); +// return PI_SUCCESS; +// } + +// void EventsWaitVerification(queue &QueueDev) { +// MockScheduler MS; + +// detail::QueueImplPtr QueueDevImpl = detail::getSyclObjImpl(QueueDev); + +// std::vector Events; + +// detail::Command *Cmd1 = AddTaskCG(true, MS, QueueDevImpl, Events); +// EventImplPtr Cmd1Event = Cmd1->getEvent(); + +// // Depends on host task +// Events.push_back(Cmd1Event); +// detail::Command *Cmd2 = AddTaskCG(false, MS, QueueDevImpl, Events); +// EventImplPtr Cmd2Event = Cmd2->getEvent(); + +// // Depends on kernel depending on host task +// Events.clear(); +// Events.push_back(Cmd2Event); +// detail::Command *Cmd3 = AddTaskCG(false, MS, QueueDevImpl, Events); +// EventImplPtr Cmd3Event = Cmd2->getEvent(); + +// detail::EnqueueResultT Result; +// EXPECT_TRUE(MS.enqueueCommand(Cmd3, Result, detail::BlockingT::NON_BLOCKING)); +// Cmd3Event->wait(Cmd3Event); + +// // One piEventsWait call: +// // kernel2 waits for kernel 1 by sending event list to enqueue launch call +// // (depending on queue property). Cmd3Event.wait() waits for kernel2 via +// // piEventsWait. +// ASSERT_EQ(PassedNumEvents.size(), 1u); +// auto [EventCount, EventArr] = PassedNumEvents[0]; +// ASSERT_EQ(EventCount, 1u); +// EXPECT_EQ(*EventArr, Cmd3Event->getHandleRef()); +// } + +// TEST_F(SchedulerTest, InOrderEnqueueNoMemObjDoubleKernelDepHost) { +// // Checks blocking command tranfer for dependent kernels and enqueue of root +// // kernel on host task completion +// unittest::ScopedEnvVar DisabledCleanup{ +// DisablePostEnqueueCleanupName, "1", +// detail::SYCLConfig::reset}; + +// unittest::PiMock Mock; +// platform Plt = Mock.getPlatform(); +// if (!CheckTestExecutionRequirements(Plt)) +// return; + +// Mock.redefine(redefinedEventsWaitCustom); +// Mock.redefine( +// redefinedEnqueueKernelLaunchCustom); + +// { +// queue QueueDev(context(Plt), default_selector_v); +// PassedNumEvents.clear(); +// PassedNumEventsToLaunch.clear(); +// EventsWaitVerification(QueueDev); +// // 1st -> kernel after host, no pi events +// // 2nd -> kernel after kernel, 1 pi event +// ASSERT_EQ(PassedNumEventsToLaunch.size(), 2u); +// { +// auto [EventCount, EventArr] = PassedNumEventsToLaunch[0]; +// EXPECT_EQ(EventCount, 0u); +// EXPECT_EQ(EventArr, nullptr); +// } +// { +// auto [EventCount, EventArr] = PassedNumEventsToLaunch[1]; +// EXPECT_EQ(EventCount, 1u); +// } +// } + +// { +// queue QueueDev(context(Plt), default_selector_v, +// property::queue::in_order()); +// PassedNumEvents.clear(); +// PassedNumEventsToLaunch.clear(); +// EventsWaitVerification(QueueDev); +// // 1st -> kernel after host, no pi events +// // 2nd -> kernel after kernel and in order queue, 0 pi event +// ASSERT_EQ(PassedNumEventsToLaunch.size(), 2u); +// { +// auto [EventCount, EventArr] = PassedNumEventsToLaunch[0]; +// EXPECT_EQ(EventCount, 0u); +// EXPECT_EQ(EventArr, nullptr); +// } +// { +// auto [EventCount, EventArr] = PassedNumEventsToLaunch[1]; +// EXPECT_EQ(EventCount, 0u); +// EXPECT_EQ(EventArr, nullptr); +// } +// } +// } \ No newline at end of file From 01898c29a160e88a99e56b5f3a188f5eac0491c5 Mon Sep 17 00:00:00 2001 From: "Tikhomirova, Kseniya" Date: Sun, 9 Oct 2022 06:10:29 -0700 Subject: [PATCH 03/22] Fix impl gaps and update/enable 1 unit test Signed-off-by: Tikhomirova, Kseniya --- sycl/source/detail/scheduler/commands.cpp | 13 +- sycl/source/detail/scheduler/commands.hpp | 17 +- .../detail/scheduler/graph_processor.cpp | 17 +- sycl/source/detail/scheduler/scheduler.cpp | 10 +- sycl/source/detail/scheduler/scheduler.hpp | 3 +- .../scheduler/EnqueueWithDependsOnDeps.cpp | 257 ++++++++++-------- 6 files changed, 180 insertions(+), 137 deletions(-) diff --git a/sycl/source/detail/scheduler/commands.cpp b/sycl/source/detail/scheduler/commands.cpp index 7603fac45c9af..5e19b80038894 100644 --- a/sycl/source/detail/scheduler/commands.cpp +++ b/sycl/source/detail/scheduler/commands.cpp @@ -335,12 +335,12 @@ class DispatchHostTask { std::vector Deps = MThisCmd->MDeps; // update self-event status - const std::vector &CmdsToEnqueue = MThisCmd->getBlockedUsers(); + const std::vector &CmdsToEnqueue = + MThisCmd->getBlockedUsers(); MThisCmd->MEvent->setComplete(); - Scheduler::enqueueUnblockedCommands(MThisCmd->MEvent, CmdsToEnqueue, - ToCleanUp); + Scheduler::enqueueUnblockedCommands(CmdsToEnqueue, ToCleanUp); for (const DepDesc &Dep : Deps) Scheduler::enqueueLeavesOfReqUnlocked(Dep.MDepRequirement, ToCleanUp); @@ -2588,13 +2588,6 @@ bool ExecCGCommand::supportsPostEnqueueCleanup() const { !static_cast(MCommandGroup.get()) ->hasAuxiliaryResources())); } - -void Command::removeBlockedUser(Command *User) { - auto it = std::find(MBlockedUsers.begin(), MBlockedUsers.end(), User); - if (it != MBlockedUsers.end()) - MBlockedUsers.erase(it); -} - } // namespace detail } // __SYCL_INLINE_VER_NAMESPACE(_V1) } // namespace sycl diff --git a/sycl/source/detail/scheduler/commands.hpp b/sycl/source/detail/scheduler/commands.hpp index 319ce6c099c99..a8b353cdbb685 100644 --- a/sycl/source/detail/scheduler/commands.hpp +++ b/sycl/source/detail/scheduler/commands.hpp @@ -149,13 +149,18 @@ class Command { } // Shows thst command could be enqueud, but is blocking enqueue of all // commands depending on it. Regular usage - host task. - bool isBlocking() const { return isHostTask() && MEvent->isComplete(); } + bool isBlocking() const { return isHostTask() && !MEvent->isComplete(); } - void addBlockedUser(Command *NewUser) { MBlockedUsers.push_back(NewUser); } + void addBlockedUser(const EventImplPtr &NewUser) { + MBlockedUsers.push_back(NewUser); + } - void removeBlockedUser(Command *User); + bool containsBlockedUser(const EventImplPtr &User) const { + return std::find(MBlockedUsers.begin(), MBlockedUsers.end(), User) != + MBlockedUsers.end(); + } - const std::vector &getBlockedUsers() const { + const std::vector &getBlockedUsers() const { return MBlockedUsers; } @@ -271,7 +276,9 @@ class Command { /// Contains list of commands that depends on the host command explicitly (by /// depends_on). Not involved into cleanup process since it is one-way link /// and not holds resources. - std::vector MBlockedUsers; + /// Using EventImplPtr since enqueueUnblockedCommands and event.wait may + /// intersect with command enqueue. + std::vector MBlockedUsers; public: const std::vector &getPreparedHostDepsEvents() const { diff --git a/sycl/source/detail/scheduler/graph_processor.cpp b/sycl/source/detail/scheduler/graph_processor.cpp index dac25c706c0cb..9a55e19a0afcd 100644 --- a/sycl/source/detail/scheduler/graph_processor.cpp +++ b/sycl/source/detail/scheduler/graph_processor.cpp @@ -50,8 +50,19 @@ bool Scheduler::GraphProcessor::enqueueCommand( Command *Cmd, EnqueueResultT &EnqueueResult, std::vector &ToCleanUp, Command *RootCommand, BlockingT Blocking) { - if (!Cmd || Cmd->isSuccessfullyEnqueued()) + + if (!Cmd) return true; + if (Cmd->isSuccessfullyEnqueued()) { + if (Cmd == RootCommand || !Cmd->isBlocking()) + return true; + const EventImplPtr &RootCmdEvent = RootCommand->getEvent(); + if (!Cmd->containsBlockedUser(RootCmdEvent)) + Cmd->addBlockedUser(RootCmdEvent); + + EnqueueResult = EnqueueResultT(EnqueueResultT::SyclEnqueueBlocked, Cmd); + return false; + } // Exit early if the command is blocked and the enqueue type is non-blocking if (Cmd->isEnqueueBlocked() && !Blocking) { @@ -95,8 +106,8 @@ bool Scheduler::GraphProcessor::enqueueCommand( // middle of enqueue of B. The other thread modifies dependency list of A by // removing C out of it. Iterators become invalid. bool Result = Cmd->enqueue(EnqueueResult, Blocking, ToCleanUp); - if (Result && Cmd->isBlocking()) { - Cmd->addBlockedUser(RootCommand); + if (Result && Cmd->isBlocking() && Cmd != RootCommand) { + Cmd->addBlockedUser(RootCommand->getEvent()); EnqueueResult = EnqueueResultT(EnqueueResultT::SyclEnqueueBlocked, Cmd); return false; } diff --git a/sycl/source/detail/scheduler/scheduler.cpp b/sycl/source/detail/scheduler/scheduler.cpp index be464a633608a..37d236a4e8545 100644 --- a/sycl/source/detail/scheduler/scheduler.cpp +++ b/sycl/source/detail/scheduler/scheduler.cpp @@ -373,12 +373,14 @@ void Scheduler::enqueueLeavesOfReqUnlocked(const Requirement *const Req, } void Scheduler::enqueueUnblockedCommands( - const EventImplPtr &UnblockedDep, const std::vector &ToEnqueue, + const std::vector &ToEnqueue, std::vector &ToCleanUp) { - for (auto &Command : ToEnqueue) { + for (auto &Event : ToEnqueue) { + Command *Cmd = static_cast(Event->getCommand()); + if (!Cmd) + continue; EnqueueResultT Res; - bool Enqueued = - GraphProcessor::enqueueCommand(Command, Res, ToCleanUp, Command); + bool Enqueued = GraphProcessor::enqueueCommand(Cmd, Res, ToCleanUp, Cmd); if (!Enqueued && EnqueueResultT::SyclEnqueueFailed == Res.MResult) throw runtime_error("Enqueue process failed.", PI_ERROR_INVALID_OPERATION); diff --git a/sycl/source/detail/scheduler/scheduler.hpp b/sycl/source/detail/scheduler/scheduler.hpp index 117241a4686cd..b5c20fbec335c 100644 --- a/sycl/source/detail/scheduler/scheduler.hpp +++ b/sycl/source/detail/scheduler/scheduler.hpp @@ -465,8 +465,7 @@ class Scheduler { std::vector &ToCleanUp); static void - enqueueUnblockedCommands(const EventImplPtr &UnblockedDep, - const std::vector &CmdsToEnqueue, + enqueueUnblockedCommands(const std::vector &CmdsToEnqueue, std::vector &ToCleanUp); /// Graph builder class. diff --git a/sycl/unittests/scheduler/EnqueueWithDependsOnDeps.cpp b/sycl/unittests/scheduler/EnqueueWithDependsOnDeps.cpp index 8b70cc0186bcc..78bceff284e7f 100644 --- a/sycl/unittests/scheduler/EnqueueWithDependsOnDeps.cpp +++ b/sycl/unittests/scheduler/EnqueueWithDependsOnDeps.cpp @@ -1,70 +1,77 @@ -// //==------------ EnqueueWithDependsOnDeps.cpp --- Scheduler unit tests------==// -// // -// // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// // See https://llvm.org/LICENSE.txt for license information. -// // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// // -// //===----------------------------------------------------------------------===// - -// #include "SchedulerTest.hpp" -// #include "SchedulerTestUtils.hpp" - -// #include -// #include -// #include - -// #include - -// using namespace sycl; -// using EventImplPtr = std::shared_ptr; - -// detail::Command *AddTaskCG(bool IsHost, MockScheduler &MS, -// detail::QueueImplPtr DevQueue, -// const std::vector &Events) { -// std::vector ToEnqueue; - -// // Emulating processing of command group function -// MockHandlerCustomFinalize MockCGH(DevQueue, false); - -// for (auto EventImpl : Events) -// MockCGH.depends_on(detail::createSyclObjFromImpl(EventImpl)); - -// if (IsHost) -// MockCGH.host_task([] {}); -// else { -// kernel_bundle KernelBundle = -// sycl::get_kernel_bundle( -// DevQueue->get_context()); -// auto ExecBundle = sycl::build(KernelBundle); -// MockCGH.use_kernel_bundle(ExecBundle); -// MockCGH.single_task>([] {}); -// } - -// std::unique_ptr CmdGroup = MockCGH.finalize(); - -// detail::Command *NewCmd = -// MS.addCG(std::move(CmdGroup), -// IsHost ? MS.getDefaultHostQueue() : DevQueue, ToEnqueue); -// EXPECT_EQ(ToEnqueue.size(), 0u); -// return NewCmd; -// } - -// bool CheckTestExecutionRequirements(const platform &plt) { -// if (plt.is_host()) { -// std::cout << "Not run due to host-only environment\n"; -// return false; -// } -// // This test only contains device image for SPIR-V capable devices. -// if (plt.get_backend() != sycl::backend::opencl && -// plt.get_backend() != sycl::backend::ext_oneapi_level_zero) { -// std::cout << "Only OpenCL and Level Zero are supported for this test\n"; -// return false; -// } -// return true; -// } - -// inline constexpr auto DisablePostEnqueueCleanupName = -// "SYCL_DISABLE_POST_ENQUEUE_CLEANUP"; +//==------------ EnqueueWithDependsOnDeps.cpp --- Scheduler unit tests------==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "SchedulerTest.hpp" +#include "SchedulerTestUtils.hpp" + +#include +#include +#include + +#include + +using namespace sycl; +using EventImplPtr = std::shared_ptr; + +enum TestCGType { KERNEL_TASK = 0x00, HOST_TASK = 0x01 }; + +detail::Command *AddTaskCG(TestCGType Type, MockScheduler &MS, + detail::QueueImplPtr DevQueue, + const std::vector &Events, + std::function *CustomHostLambda = nullptr) { + std::vector ToEnqueue; + + // Emulating processing of command group function + MockHandlerCustomFinalize MockCGH(DevQueue, false); + + for (auto EventImpl : Events) + MockCGH.depends_on(detail::createSyclObjFromImpl(EventImpl)); + + if (Type == TestCGType::HOST_TASK) { + if (!CustomHostLambda) + MockCGH.host_task([] {}); + else + MockCGH.host_task(*CustomHostLambda); + } else { + kernel_bundle KernelBundle = + sycl::get_kernel_bundle( + DevQueue->get_context()); + auto ExecBundle = sycl::build(KernelBundle); + MockCGH.use_kernel_bundle(ExecBundle); + MockCGH.single_task>([] {}); + } + + std::unique_ptr CmdGroup = MockCGH.finalize(); + + detail::Command *NewCmd = MS.addCG( + std::move(CmdGroup), + Type == TestCGType::HOST_TASK ? MS.getDefaultHostQueue() : DevQueue, + ToEnqueue); + EXPECT_EQ(ToEnqueue.size(), 0u); + return NewCmd; +} + +bool CheckTestExecutionRequirements(const platform &plt) { + if (plt.is_host()) { + std::cout << "Not run due to host-only environment\n"; + return false; + } + // This test only contains device image for SPIR-V capable devices. + if (plt.get_backend() != sycl::backend::opencl && + plt.get_backend() != sycl::backend::ext_oneapi_level_zero) { + std::cout << "Only OpenCL and Level Zero are supported for this test\n"; + return false; + } + return true; +} + +inline constexpr auto DisablePostEnqueueCleanupName = + "SYCL_DISABLE_POST_ENQUEUE_CLEANUP"; // TEST_F(SchedulerTest, EnqueueNoMemObjTwoHostTasks) { // // Checks enqueue of two dependent host tasks @@ -86,16 +93,17 @@ // std::vector Events; -// detail::Command *Cmd1 = AddTaskCG(true, MS, QueueDevImpl, Events); -// EventImplPtr Cmd1Event = Cmd1->getEvent(); +// detail::Command *Cmd1 = AddTaskCG(TestCGType::HOST_TASK, MS, QueueDevImpl, +// Events); EventImplPtr Cmd1Event = Cmd1->getEvent(); // // Simulate depends_on() call // Events.push_back(Cmd1Event); -// detail::Command *Cmd2 = AddTaskCG(true, MS, QueueDevImpl, Events); -// EventImplPtr Cmd2Event = Cmd2->getEvent(); +// detail::Command *Cmd2 = AddTaskCG(TestCGType::HOST_TASK, MS, QueueDevImpl, +// Events); EventImplPtr Cmd2Event = Cmd2->getEvent(); // detail::EnqueueResultT Result; -// EXPECT_TRUE(MS.enqueueCommand(Cmd2, Result, detail::BlockingT::NON_BLOCKING)); +// EXPECT_TRUE(MS.enqueueCommand(Cmd2, Result, +// detail::BlockingT::NON_BLOCKING)); // // Preconditions for post enqueue checks // EXPECT_TRUE(Cmd1->isSuccessfullyEnqueued()); @@ -108,41 +116,60 @@ // info::event_command_status::complete); // } -// TEST_F(SchedulerTest, EnqueueNoMemObjKernelDepHost) { -// // Checks enqueue of kernel depending on host task -// unittest::ScopedEnvVar DisabledCleanup{ -// DisablePostEnqueueCleanupName, "1", -// detail::SYCLConfig::reset}; - -// unittest::PiMock Mock; -// platform Plt = Mock.getPlatform(); -// if (!CheckTestExecutionRequirements(Plt)) -// return; - -// queue QueueDev(context(Plt), default_selector_v); -// MockScheduler MS; - -// detail::QueueImplPtr QueueDevImpl = detail::getSyclObjImpl(QueueDev); - -// std::vector Events; - -// detail::Command *Cmd1 = AddTaskCG(true, MS, QueueDevImpl, Events); -// EventImplPtr Cmd1Event = Cmd1->getEvent(); - -// // Simulate depends_on() call -// Events.push_back(Cmd1Event); -// detail::Command *Cmd2 = AddTaskCG(false, MS, QueueDevImpl, Events); -// EventImplPtr Cmd2Event = Cmd2->getEvent(); - -// detail::EnqueueResultT Result; -// EXPECT_TRUE(MS.enqueueCommand(Cmd2, Result, detail::BlockingT::NON_BLOCKING)); - -// // Preconditions for post enqueue checks -// EXPECT_TRUE(Cmd1->isSuccessfullyEnqueued()); -// EXPECT_TRUE(Cmd2->isSuccessfullyEnqueued()); - -// Cmd2Event->wait(Cmd2Event); -// } +TEST_F(SchedulerTest, EnqueueNoMemObjKernelDepHost) { + // Checks enqueue of kernel depending on host task + unittest::ScopedEnvVar DisabledCleanup{ + DisablePostEnqueueCleanupName, "1", + detail::SYCLConfig::reset}; + + unittest::PiMock Mock; + platform Plt = Mock.getPlatform(); + if (!CheckTestExecutionRequirements(Plt)) + return; + + queue QueueDev(context(Plt), default_selector_v); + MockScheduler MS; + + detail::QueueImplPtr QueueDevImpl = detail::getSyclObjImpl(QueueDev); + + std::vector Events; + + std::mutex m; + std::function CustomHostLambda = [&m]() { + std::unique_lock InsideHostTaskLock(m); + }; + + detail::Command *Cmd1 = AddTaskCG(TestCGType::HOST_TASK, MS, QueueDevImpl, + Events, &CustomHostLambda); + EventImplPtr Cmd1Event = Cmd1->getEvent(); + EXPECT_TRUE(Cmd1->isBlocking()); + + // Simulate depends_on() call + Events.push_back(Cmd1Event); + detail::Command *Cmd2 = + AddTaskCG(TestCGType::KERNEL_TASK, MS, QueueDevImpl, Events); + EventImplPtr Cmd2Event = Cmd2->getEvent(); + + std::unique_lock TestLock(m, std::defer_lock); + TestLock.lock(); + detail::EnqueueResultT Result; + EXPECT_FALSE( + MS.enqueueCommand(Cmd2, Result, detail::BlockingT::NON_BLOCKING)); + EXPECT_EQ(Result.MResult, detail::EnqueueResultT::SyclEnqueueBlocked); + EXPECT_EQ(Result.MCmd, static_cast(Cmd1)); + EXPECT_TRUE(Cmd1->isSuccessfullyEnqueued()); + EXPECT_FALSE(Cmd2->isSuccessfullyEnqueued()); + + TestLock.unlock(); + Cmd1Event->wait(Cmd1Event); + { + auto Lock = MS.acquireOriginSchedGraphWriteLock(); + Lock.lock(); + EXPECT_TRUE(Cmd2->isSuccessfullyEnqueued()); + } + + Cmd2Event->wait(Cmd2Event); +} // TEST_F(SchedulerTest, EnqueueNoMemObjHostDepKernel) { // // Checks enqueue of host task depending on kernel @@ -171,7 +198,8 @@ // EventImplPtr Cmd2Event = Cmd2->getEvent(); // detail::EnqueueResultT Result; -// EXPECT_TRUE(MS.enqueueCommand(Cmd2, Result, detail::BlockingT::NON_BLOCKING)); +// EXPECT_TRUE(MS.enqueueCommand(Cmd2, Result, +// detail::BlockingT::NON_BLOCKING)); // // Preconditions for post enqueue checks // EXPECT_TRUE(Cmd1->isSuccessfullyEnqueued()); @@ -180,7 +208,8 @@ // } // TEST_F(SchedulerTest, EnqueueNoMemObjDoubleKernelDepHostBlocked) { -// // Checks blocking command tranfer for dependent kernels and enqueue of root +// // Checks blocking command tranfer for dependent kernels and enqueue of +// root // // kernel on host task completion // unittest::ScopedEnvVar DisabledCleanup{ // DisablePostEnqueueCleanupName, "1", @@ -230,7 +259,8 @@ // Cmd1->MEnqueueStatus = detail::EnqueueResultT::SyclEnqueueReady; -// EXPECT_TRUE(MS.enqueueCommand(Cmd3, Result, detail::BlockingT::NON_BLOCKING)); +// EXPECT_TRUE(MS.enqueueCommand(Cmd3, Result, +// detail::BlockingT::NON_BLOCKING)); // EXPECT_TRUE(Cmd1->isSuccessfullyEnqueued()); // EXPECT_TRUE(Cmd2->isSuccessfullyEnqueued()); @@ -278,8 +308,8 @@ // EventImplPtr Cmd3Event = Cmd2->getEvent(); // detail::EnqueueResultT Result; -// EXPECT_TRUE(MS.enqueueCommand(Cmd3, Result, detail::BlockingT::NON_BLOCKING)); -// Cmd3Event->wait(Cmd3Event); +// EXPECT_TRUE(MS.enqueueCommand(Cmd3, Result, +// detail::BlockingT::NON_BLOCKING)); Cmd3Event->wait(Cmd3Event); // // One piEventsWait call: // // kernel2 waits for kernel 1 by sending event list to enqueue launch call @@ -292,7 +322,8 @@ // } // TEST_F(SchedulerTest, InOrderEnqueueNoMemObjDoubleKernelDepHost) { -// // Checks blocking command tranfer for dependent kernels and enqueue of root +// // Checks blocking command tranfer for dependent kernels and enqueue of +// root // // kernel on host task completion // unittest::ScopedEnvVar DisabledCleanup{ // DisablePostEnqueueCleanupName, "1", From 007fbb747d77b922746b01bac6121c45d8017f89 Mon Sep 17 00:00:00 2001 From: "Tikhomirova, Kseniya" Date: Mon, 10 Oct 2022 06:19:31 -0700 Subject: [PATCH 04/22] Fix & enable unit tests Signed-off-by: Tikhomirova, Kseniya --- .../scheduler/EnqueueWithDependsOnDeps.cpp | 527 ++++++++---------- 1 file changed, 218 insertions(+), 309 deletions(-) diff --git a/sycl/unittests/scheduler/EnqueueWithDependsOnDeps.cpp b/sycl/unittests/scheduler/EnqueueWithDependsOnDeps.cpp index 78bceff284e7f..29f99534255f5 100644 --- a/sycl/unittests/scheduler/EnqueueWithDependsOnDeps.cpp +++ b/sycl/unittests/scheduler/EnqueueWithDependsOnDeps.cpp @@ -18,43 +18,10 @@ using namespace sycl; using EventImplPtr = std::shared_ptr; -enum TestCGType { KERNEL_TASK = 0x00, HOST_TASK = 0x01 }; - -detail::Command *AddTaskCG(TestCGType Type, MockScheduler &MS, - detail::QueueImplPtr DevQueue, - const std::vector &Events, - std::function *CustomHostLambda = nullptr) { - std::vector ToEnqueue; - - // Emulating processing of command group function - MockHandlerCustomFinalize MockCGH(DevQueue, false); - - for (auto EventImpl : Events) - MockCGH.depends_on(detail::createSyclObjFromImpl(EventImpl)); - - if (Type == TestCGType::HOST_TASK) { - if (!CustomHostLambda) - MockCGH.host_task([] {}); - else - MockCGH.host_task(*CustomHostLambda); - } else { - kernel_bundle KernelBundle = - sycl::get_kernel_bundle( - DevQueue->get_context()); - auto ExecBundle = sycl::build(KernelBundle); - MockCGH.use_kernel_bundle(ExecBundle); - MockCGH.single_task>([] {}); - } +constexpr auto DisablePostEnqueueCleanupName = + "SYCL_DISABLE_POST_ENQUEUE_CLEANUP"; - std::unique_ptr CmdGroup = MockCGH.finalize(); - - detail::Command *NewCmd = MS.addCG( - std::move(CmdGroup), - Type == TestCGType::HOST_TASK ? MS.getDefaultHostQueue() : DevQueue, - ToEnqueue); - EXPECT_EQ(ToEnqueue.size(), 0u); - return NewCmd; -} +std::vector> PassedNumEvents; bool CheckTestExecutionRequirements(const platform &plt) { if (plt.is_host()) { @@ -70,311 +37,253 @@ bool CheckTestExecutionRequirements(const platform &plt) { return true; } -inline constexpr auto DisablePostEnqueueCleanupName = - "SYCL_DISABLE_POST_ENQUEUE_CLEANUP"; +enum TestCGType { KERNEL_TASK = 0x00, HOST_TASK = 0x01 }; -// TEST_F(SchedulerTest, EnqueueNoMemObjTwoHostTasks) { -// // Checks enqueue of two dependent host tasks +class DependsOnTests : public ::testing::Test { +protected: + void SetUp() { + platform Plt = Mock.getPlatform(); + if (!CheckTestExecutionRequirements(Plt)) + GTEST_SKIP(); -// unittest::ScopedEnvVar DisabledCleanup{ -// DisablePostEnqueueCleanupName, "1", -// detail::SYCLConfig::reset}; + queue QueueDev(context(Plt), default_selector_v); + QueueDevImpl = detail::getSyclObjImpl(QueueDev); + } -// unittest::PiMock Mock; -// platform Plt = Mock.getPlatform(); -// if (!CheckTestExecutionRequirements(Plt)) -// return; + void TearDown() {} + + detail::Command * + AddTaskCG(TestCGType Type, const std::vector &Events, + std::function *CustomHostLambda = nullptr) { + std::vector ToEnqueue; + + // Emulating processing of command group function + MockHandlerCustomFinalize MockCGH(QueueDevImpl, false); + + for (auto EventImpl : Events) + MockCGH.depends_on(detail::createSyclObjFromImpl(EventImpl)); + + if (Type == TestCGType::HOST_TASK) { + if (!CustomHostLambda) + MockCGH.host_task([] {}); + else + MockCGH.host_task(*CustomHostLambda); + } else { + kernel_bundle KernelBundle = + sycl::get_kernel_bundle( + QueueDevImpl->get_context()); + auto ExecBundle = sycl::build(KernelBundle); + MockCGH.use_kernel_bundle(ExecBundle); + MockCGH.single_task>([] {}); + } + + std::unique_ptr CmdGroup = MockCGH.finalize(); + + detail::Command *NewCmd = MS.addCG( + std::move(CmdGroup), + Type == TestCGType::HOST_TASK ? MS.getDefaultHostQueue() : QueueDevImpl, + ToEnqueue); + EXPECT_EQ(ToEnqueue.size(), 0u); + return NewCmd; + } -// queue QueueDev(context(Plt), default_selector_v); -// MockScheduler MS; + void EventsWaitVerification() { + std::vector Events; + + detail::Command *Cmd1 = AddTaskCG(TestCGType::HOST_TASK, Events); + EventImplPtr Cmd1Event = Cmd1->getEvent(); + + // Depends on host task + Events.push_back(Cmd1Event); + detail::Command *Cmd2 = AddTaskCG(TestCGType::KERNEL_TASK, Events); + EventImplPtr Cmd2Event = Cmd2->getEvent(); + + // Depends on kernel depending on host task + Events.clear(); + Events.push_back(Cmd2Event); + detail::Command *Cmd3 = AddTaskCG(TestCGType::KERNEL_TASK, Events); + EventImplPtr Cmd3Event = Cmd3->getEvent(); + + std::vector BlockedCommands{Cmd2, Cmd3}; + VerifyBlockedCommandsEnqueue(Cmd1, BlockedCommands); + + // One piEventsWait call: + // kernel2 waits for kernel 1 by sending event list to enqueue launch call + // (depending on queue property). Cmd3Event.wait() waits for kernel2 via + // piEventsWait. + ASSERT_EQ(PassedNumEvents.size(), 1u); + auto [EventCount, EventArr] = PassedNumEvents[0]; + ASSERT_EQ(EventCount, 1u); + EXPECT_EQ(*EventArr, Cmd3Event->getHandleRef()); + } -// detail::QueueImplPtr QueueDevImpl = detail::getSyclObjImpl(QueueDev); -// detail::QueueImplPtr QueueHostImpl = MS.getDefaultHostQueue(); + void VerifyBlockedCommandsEnqueue( + detail::Command *BlockingCommand, + std::vector &BlockedCommands) { + std::unique_lock TestLock(m, std::defer_lock); + TestLock.lock(); + detail::EnqueueResultT Result; + for (detail::Command *BlockedCmd : BlockedCommands) { + EXPECT_FALSE(MS.enqueueCommand(BlockedCmd, Result, + detail::BlockingT::NON_BLOCKING)); + EXPECT_EQ(Result.MResult, detail::EnqueueResultT::SyclEnqueueBlocked); + EXPECT_EQ(Result.MCmd, static_cast(BlockingCommand)); + EXPECT_FALSE(BlockedCmd->isSuccessfullyEnqueued()); + } + EXPECT_TRUE(BlockingCommand->isSuccessfullyEnqueued()); + + TestLock.unlock(); + + auto BlockingEvent = BlockingCommand->getEvent(); + BlockingEvent->wait(BlockingEvent); + { + auto Lock = MS.acquireOriginSchedGraphWriteLock(); + Lock.lock(); + for (detail::Command *BlockedCmd : BlockedCommands) { + EXPECT_TRUE(BlockedCmd->isSuccessfullyEnqueued()); + } + } + for (detail::Command *BlockedCmd : BlockedCommands) { + auto BlockedEvent = BlockedCmd->getEvent(); + BlockedEvent->wait(BlockedEvent); + } + } -// std::vector Events; + unittest::PiMock Mock; + unittest::ScopedEnvVar DisabledCleanup{ + DisablePostEnqueueCleanupName, "1", + detail::SYCLConfig::reset}; + MockScheduler MS; -// detail::Command *Cmd1 = AddTaskCG(TestCGType::HOST_TASK, MS, QueueDevImpl, -// Events); EventImplPtr Cmd1Event = Cmd1->getEvent(); + detail::QueueImplPtr QueueDevImpl; -// // Simulate depends_on() call -// Events.push_back(Cmd1Event); -// detail::Command *Cmd2 = AddTaskCG(TestCGType::HOST_TASK, MS, QueueDevImpl, -// Events); EventImplPtr Cmd2Event = Cmd2->getEvent(); + std::mutex m; + std::function CustomHostLambda = [&]() { + std::unique_lock InsideHostTaskLock(this->m); + }; +}; -// detail::EnqueueResultT Result; -// EXPECT_TRUE(MS.enqueueCommand(Cmd2, Result, -// detail::BlockingT::NON_BLOCKING)); +TEST_F(DependsOnTests, EnqueueNoMemObjTwoHostTasks) { + // Checks enqueue of two dependent host tasks + detail::QueueImplPtr QueueHostImpl = MS.getDefaultHostQueue(); + std::vector Events; -// // Preconditions for post enqueue checks -// EXPECT_TRUE(Cmd1->isSuccessfullyEnqueued()); -// EXPECT_TRUE(Cmd2->isSuccessfullyEnqueued()); + detail::Command *Cmd1 = + AddTaskCG(TestCGType::HOST_TASK, Events, &CustomHostLambda); + EventImplPtr Cmd1Event = Cmd1->getEvent(); -// Cmd2Event->wait(Cmd2Event); -// EXPECT_EQ(Cmd1Event->get_info(), -// info::event_command_status::complete); -// EXPECT_EQ(Cmd2Event->get_info(), -// info::event_command_status::complete); -// } + // Simulate depends_on() call + Events.push_back(Cmd1Event); + detail::Command *Cmd2 = AddTaskCG(TestCGType::HOST_TASK, Events); + EventImplPtr Cmd2Event = Cmd2->getEvent(); + + std::vector BlockedCommands{Cmd2}; + VerifyBlockedCommandsEnqueue(Cmd1, BlockedCommands); + EXPECT_EQ(Cmd1Event->get_info(), + info::event_command_status::complete); + EXPECT_EQ(Cmd2Event->get_info(), + info::event_command_status::complete); +} -TEST_F(SchedulerTest, EnqueueNoMemObjKernelDepHost) { +TEST_F(DependsOnTests, EnqueueNoMemObjKernelDepHost) { // Checks enqueue of kernel depending on host task - unittest::ScopedEnvVar DisabledCleanup{ - DisablePostEnqueueCleanupName, "1", - detail::SYCLConfig::reset}; + std::vector Events; - unittest::PiMock Mock; - platform Plt = Mock.getPlatform(); - if (!CheckTestExecutionRequirements(Plt)) - return; + detail::Command *Cmd1 = + AddTaskCG(TestCGType::HOST_TASK, Events, &CustomHostLambda); + EventImplPtr Cmd1Event = Cmd1->getEvent(); + EXPECT_TRUE(Cmd1->isBlocking()); - queue QueueDev(context(Plt), default_selector_v); - MockScheduler MS; + // Simulate depends_on() call + Events.push_back(Cmd1Event); + detail::Command *Cmd2 = AddTaskCG(TestCGType::KERNEL_TASK, Events); - detail::QueueImplPtr QueueDevImpl = detail::getSyclObjImpl(QueueDev); + std::vector BlockedCommands{Cmd2}; + VerifyBlockedCommandsEnqueue(Cmd1, BlockedCommands); +} +TEST_F(DependsOnTests, EnqueueNoMemObjHostDepKernel) { + // Checks enqueue of host task depending on kernel std::vector Events; - std::mutex m; - std::function CustomHostLambda = [&m]() { - std::unique_lock InsideHostTaskLock(m); - }; - - detail::Command *Cmd1 = AddTaskCG(TestCGType::HOST_TASK, MS, QueueDevImpl, - Events, &CustomHostLambda); + detail::Command *Cmd1 = AddTaskCG(TestCGType::KERNEL_TASK, Events); EventImplPtr Cmd1Event = Cmd1->getEvent(); - EXPECT_TRUE(Cmd1->isBlocking()); // Simulate depends_on() call Events.push_back(Cmd1Event); - detail::Command *Cmd2 = - AddTaskCG(TestCGType::KERNEL_TASK, MS, QueueDevImpl, Events); + detail::Command *Cmd2 = AddTaskCG(TestCGType::HOST_TASK, Events); EventImplPtr Cmd2Event = Cmd2->getEvent(); - std::unique_lock TestLock(m, std::defer_lock); - TestLock.lock(); + detail::EnqueueResultT Result; + EXPECT_TRUE(MS.enqueueCommand(Cmd2, Result, detail::BlockingT::NON_BLOCKING)); + + EXPECT_TRUE(Cmd1->isSuccessfullyEnqueued()); + EXPECT_TRUE(Cmd2->isSuccessfullyEnqueued()); + Cmd2Event->wait(Cmd2Event); +} + +TEST_F(DependsOnTests, EnqueueNoMemObjDoubleKernelDepHostBlocked) { + // Checks blocking command tranfer for dependent kernels and enqueue of + // kernels on host task completion + std::vector Events; + + detail::Command *Cmd1 = AddTaskCG(TestCGType::HOST_TASK, Events); + EventImplPtr Cmd1Event = Cmd1->getEvent(); + Cmd1->MIsBlockable = true; + Cmd1->MEnqueueStatus = detail::EnqueueResultT::SyclEnqueueBlocked; + + // Depends on host task + Events.push_back(Cmd1Event); + detail::Command *Cmd2 = AddTaskCG(TestCGType::KERNEL_TASK, Events); + EventImplPtr Cmd2Event = Cmd2->getEvent(); + + // Depends on kernel depending on host task + Events.clear(); + Events.push_back(Cmd2Event); + detail::Command *Cmd3 = AddTaskCG(TestCGType::KERNEL_TASK, Events); + EventImplPtr Cmd3Event = Cmd3->getEvent(); + detail::EnqueueResultT Result; EXPECT_FALSE( MS.enqueueCommand(Cmd2, Result, detail::BlockingT::NON_BLOCKING)); EXPECT_EQ(Result.MResult, detail::EnqueueResultT::SyclEnqueueBlocked); EXPECT_EQ(Result.MCmd, static_cast(Cmd1)); - EXPECT_TRUE(Cmd1->isSuccessfullyEnqueued()); + EXPECT_FALSE( + MS.enqueueCommand(Cmd3, Result, detail::BlockingT::NON_BLOCKING)); + EXPECT_EQ(Result.MResult, detail::EnqueueResultT::SyclEnqueueBlocked); + EXPECT_EQ(Result.MCmd, static_cast(Cmd1)); + + // Preconditions for post enqueue checks + EXPECT_FALSE(Cmd1->isSuccessfullyEnqueued()); EXPECT_FALSE(Cmd2->isSuccessfullyEnqueued()); + EXPECT_FALSE(Cmd3->isSuccessfullyEnqueued()); - TestLock.unlock(); - Cmd1Event->wait(Cmd1Event); - { - auto Lock = MS.acquireOriginSchedGraphWriteLock(); - Lock.lock(); - EXPECT_TRUE(Cmd2->isSuccessfullyEnqueued()); - } + Cmd1->MEnqueueStatus = detail::EnqueueResultT::SyclEnqueueReady; - Cmd2Event->wait(Cmd2Event); + std::vector BlockedCommands{Cmd2, Cmd3}; + VerifyBlockedCommandsEnqueue(Cmd1, BlockedCommands); } -// TEST_F(SchedulerTest, EnqueueNoMemObjHostDepKernel) { -// // Checks enqueue of host task depending on kernel -// unittest::ScopedEnvVar DisabledCleanup{ -// DisablePostEnqueueCleanupName, "1", -// detail::SYCLConfig::reset}; - -// unittest::PiMock Mock; -// platform Plt = Mock.getPlatform(); -// if (!CheckTestExecutionRequirements(Plt)) -// return; - -// queue QueueDev(context(Plt), default_selector_v); -// MockScheduler MS; - -// detail::QueueImplPtr QueueDevImpl = detail::getSyclObjImpl(QueueDev); - -// std::vector Events; - -// detail::Command *Cmd1 = AddTaskCG(false, MS, QueueDevImpl, Events); -// EventImplPtr Cmd1Event = Cmd1->getEvent(); - -// // Simulate depends_on() call -// Events.push_back(Cmd1Event); -// detail::Command *Cmd2 = AddTaskCG(true, MS, QueueDevImpl, Events); -// EventImplPtr Cmd2Event = Cmd2->getEvent(); - -// detail::EnqueueResultT Result; -// EXPECT_TRUE(MS.enqueueCommand(Cmd2, Result, -// detail::BlockingT::NON_BLOCKING)); - -// // Preconditions for post enqueue checks -// EXPECT_TRUE(Cmd1->isSuccessfullyEnqueued()); -// EXPECT_TRUE(Cmd2->isSuccessfullyEnqueued()); -// Cmd2Event->wait(Cmd2Event); -// } - -// TEST_F(SchedulerTest, EnqueueNoMemObjDoubleKernelDepHostBlocked) { -// // Checks blocking command tranfer for dependent kernels and enqueue of -// root -// // kernel on host task completion -// unittest::ScopedEnvVar DisabledCleanup{ -// DisablePostEnqueueCleanupName, "1", -// detail::SYCLConfig::reset}; - -// unittest::PiMock Mock; -// platform Plt = Mock.getPlatform(); -// if (!CheckTestExecutionRequirements(Plt)) -// return; - -// queue QueueDev(context(Plt), default_selector_v); -// MockScheduler MS; - -// detail::QueueImplPtr QueueDevImpl = detail::getSyclObjImpl(QueueDev); - -// std::vector Events; - -// detail::Command *Cmd1 = AddTaskCG(true, MS, QueueDevImpl, Events); -// EventImplPtr Cmd1Event = Cmd1->getEvent(); -// Cmd1->MEnqueueStatus = detail::EnqueueResultT::SyclEnqueueBlocked; - -// // Depends on host task -// Events.push_back(Cmd1Event); -// detail::Command *Cmd2 = AddTaskCG(false, MS, QueueDevImpl, Events); -// EventImplPtr Cmd2Event = Cmd2->getEvent(); - -// // Depends on kernel depending on host task -// Events.clear(); -// Events.push_back(Cmd2Event); -// detail::Command *Cmd3 = AddTaskCG(false, MS, QueueDevImpl, Events); -// EventImplPtr Cmd3Event = Cmd2->getEvent(); - -// detail::EnqueueResultT Result; -// EXPECT_FALSE( -// MS.enqueueCommand(Cmd2, Result, detail::BlockingT::NON_BLOCKING)); -// EXPECT_EQ(Result.MResult, detail::EnqueueResultT::SyclEnqueueBlocked); -// EXPECT_EQ(Result.MCmd, static_cast(Cmd1)); -// EXPECT_FALSE( -// MS.enqueueCommand(Cmd3, Result, detail::BlockingT::NON_BLOCKING)); -// EXPECT_EQ(Result.MResult, detail::EnqueueResultT::SyclEnqueueBlocked); -// EXPECT_EQ(Result.MCmd, static_cast(Cmd1)); - -// // Preconditions for post enqueue checks -// EXPECT_FALSE(Cmd1->isSuccessfullyEnqueued()); -// EXPECT_FALSE(Cmd2->isSuccessfullyEnqueued()); -// EXPECT_FALSE(Cmd3->isSuccessfullyEnqueued()); - -// Cmd1->MEnqueueStatus = detail::EnqueueResultT::SyclEnqueueReady; - -// EXPECT_TRUE(MS.enqueueCommand(Cmd3, Result, -// detail::BlockingT::NON_BLOCKING)); - -// EXPECT_TRUE(Cmd1->isSuccessfullyEnqueued()); -// EXPECT_TRUE(Cmd2->isSuccessfullyEnqueued()); -// EXPECT_TRUE(Cmd3->isSuccessfullyEnqueued()); - -// Cmd3Event->wait(Cmd2Event); -// } - -// std::vector> PassedNumEvents; -// inline pi_result redefinedEventsWaitCustom(pi_uint32 num_events, -// const pi_event *event_list) { -// PassedNumEvents.push_back(std::make_pair(num_events, event_list)); -// return PI_SUCCESS; -// } - -// std::vector> PassedNumEventsToLaunch; -// inline pi_result redefinedEnqueueKernelLaunchCustom( -// pi_queue, pi_kernel, pi_uint32, const size_t *, const size_t *, -// const size_t *, pi_uint32 num_events, const pi_event *event_list, -// pi_event *event) { -// PassedNumEventsToLaunch.push_back(std::make_pair(num_events, event_list)); -// *event = reinterpret_cast(new int{}); -// return PI_SUCCESS; -// } - -// void EventsWaitVerification(queue &QueueDev) { -// MockScheduler MS; - -// detail::QueueImplPtr QueueDevImpl = detail::getSyclObjImpl(QueueDev); - -// std::vector Events; - -// detail::Command *Cmd1 = AddTaskCG(true, MS, QueueDevImpl, Events); -// EventImplPtr Cmd1Event = Cmd1->getEvent(); - -// // Depends on host task -// Events.push_back(Cmd1Event); -// detail::Command *Cmd2 = AddTaskCG(false, MS, QueueDevImpl, Events); -// EventImplPtr Cmd2Event = Cmd2->getEvent(); - -// // Depends on kernel depending on host task -// Events.clear(); -// Events.push_back(Cmd2Event); -// detail::Command *Cmd3 = AddTaskCG(false, MS, QueueDevImpl, Events); -// EventImplPtr Cmd3Event = Cmd2->getEvent(); - -// detail::EnqueueResultT Result; -// EXPECT_TRUE(MS.enqueueCommand(Cmd3, Result, -// detail::BlockingT::NON_BLOCKING)); Cmd3Event->wait(Cmd3Event); - -// // One piEventsWait call: -// // kernel2 waits for kernel 1 by sending event list to enqueue launch call -// // (depending on queue property). Cmd3Event.wait() waits for kernel2 via -// // piEventsWait. -// ASSERT_EQ(PassedNumEvents.size(), 1u); -// auto [EventCount, EventArr] = PassedNumEvents[0]; -// ASSERT_EQ(EventCount, 1u); -// EXPECT_EQ(*EventArr, Cmd3Event->getHandleRef()); -// } - -// TEST_F(SchedulerTest, InOrderEnqueueNoMemObjDoubleKernelDepHost) { -// // Checks blocking command tranfer for dependent kernels and enqueue of -// root -// // kernel on host task completion -// unittest::ScopedEnvVar DisabledCleanup{ -// DisablePostEnqueueCleanupName, "1", -// detail::SYCLConfig::reset}; - -// unittest::PiMock Mock; -// platform Plt = Mock.getPlatform(); -// if (!CheckTestExecutionRequirements(Plt)) -// return; - -// Mock.redefine(redefinedEventsWaitCustom); -// Mock.redefine( -// redefinedEnqueueKernelLaunchCustom); - -// { -// queue QueueDev(context(Plt), default_selector_v); -// PassedNumEvents.clear(); -// PassedNumEventsToLaunch.clear(); -// EventsWaitVerification(QueueDev); -// // 1st -> kernel after host, no pi events -// // 2nd -> kernel after kernel, 1 pi event -// ASSERT_EQ(PassedNumEventsToLaunch.size(), 2u); -// { -// auto [EventCount, EventArr] = PassedNumEventsToLaunch[0]; -// EXPECT_EQ(EventCount, 0u); -// EXPECT_EQ(EventArr, nullptr); -// } -// { -// auto [EventCount, EventArr] = PassedNumEventsToLaunch[1]; -// EXPECT_EQ(EventCount, 1u); -// } -// } - -// { -// queue QueueDev(context(Plt), default_selector_v, -// property::queue::in_order()); -// PassedNumEvents.clear(); -// PassedNumEventsToLaunch.clear(); -// EventsWaitVerification(QueueDev); -// // 1st -> kernel after host, no pi events -// // 2nd -> kernel after kernel and in order queue, 0 pi event -// ASSERT_EQ(PassedNumEventsToLaunch.size(), 2u); -// { -// auto [EventCount, EventArr] = PassedNumEventsToLaunch[0]; -// EXPECT_EQ(EventCount, 0u); -// EXPECT_EQ(EventArr, nullptr); -// } -// { -// auto [EventCount, EventArr] = PassedNumEventsToLaunch[1]; -// EXPECT_EQ(EventCount, 0u); -// EXPECT_EQ(EventArr, nullptr); -// } -// } -// } \ No newline at end of file +TEST_F(DependsOnTests, EnqueueNoMemObjDoubleKernelDepHost) { + // Checks blocking command tranfer for dependent kernels and enqueue of + // kernels on host task completion + std::vector Events; + + detail::Command *Cmd1 = AddTaskCG(TestCGType::HOST_TASK, Events); + EventImplPtr Cmd1Event = Cmd1->getEvent(); + Cmd1->MEnqueueStatus = detail::EnqueueResultT::SyclEnqueueBlocked; + + // Depends on host task + Events.push_back(Cmd1Event); + detail::Command *Cmd2 = AddTaskCG(TestCGType::KERNEL_TASK, Events); + EventImplPtr Cmd2Event = Cmd2->getEvent(); + + // Depends on kernel depending on host task + Events.clear(); + Events.push_back(Cmd2Event); + detail::Command *Cmd3 = AddTaskCG(TestCGType::KERNEL_TASK, Events); + EventImplPtr Cmd3Event = Cmd3->getEvent(); + + std::vector BlockedCommands{Cmd2, Cmd3}; + VerifyBlockedCommandsEnqueue(Cmd1, BlockedCommands); +} \ No newline at end of file From d442362ee254620f1314f7e018c39241a7432883 Mon Sep 17 00:00:00 2001 From: "Tikhomirova, Kseniya" Date: Mon, 10 Oct 2022 11:46:30 -0700 Subject: [PATCH 05/22] Fix blocking enqueueCommand call & update queue unit tests Signed-off-by: Tikhomirova, Kseniya --- .../detail/scheduler/graph_processor.cpp | 33 ++++++++++--------- sycl/source/detail/scheduler/scheduler.hpp | 3 ++ sycl/unittests/queue/Wait.cpp | 26 ++++++++------- 3 files changed, 36 insertions(+), 26 deletions(-) diff --git a/sycl/source/detail/scheduler/graph_processor.cpp b/sycl/source/detail/scheduler/graph_processor.cpp index 9a55e19a0afcd..46575299032e9 100644 --- a/sycl/source/detail/scheduler/graph_processor.cpp +++ b/sycl/source/detail/scheduler/graph_processor.cpp @@ -46,23 +46,29 @@ void Scheduler::GraphProcessor::waitForEvent(EventImplPtr Event, GraphReadLock.lock(); } -bool Scheduler::GraphProcessor::enqueueCommand( - Command *Cmd, EnqueueResultT &EnqueueResult, - std::vector &ToCleanUp, Command *RootCommand, - BlockingT Blocking) { - - if (!Cmd) +bool Scheduler::GraphProcessor::handleBlockingCmd(Command *Cmd, + EnqueueResultT &EnqueueResult, + Command *RootCommand, + BlockingT Blocking) { + if (Cmd == RootCommand || !Cmd->isBlocking() || Blocking) return true; - if (Cmd->isSuccessfullyEnqueued()) { - if (Cmd == RootCommand || !Cmd->isBlocking()) - return true; + if (!Blocking) { const EventImplPtr &RootCmdEvent = RootCommand->getEvent(); if (!Cmd->containsBlockedUser(RootCmdEvent)) Cmd->addBlockedUser(RootCmdEvent); - EnqueueResult = EnqueueResultT(EnqueueResultT::SyclEnqueueBlocked, Cmd); return false; } +} + +bool Scheduler::GraphProcessor::enqueueCommand( + Command *Cmd, EnqueueResultT &EnqueueResult, + std::vector &ToCleanUp, Command *RootCommand, + BlockingT Blocking) { + if (!Cmd) + return true; + if (Cmd->isSuccessfullyEnqueued()) + return handleBlockingCmd(Cmd, EnqueueResult, RootCommand, Blocking); // Exit early if the command is blocked and the enqueue type is non-blocking if (Cmd->isEnqueueBlocked() && !Blocking) { @@ -106,11 +112,8 @@ bool Scheduler::GraphProcessor::enqueueCommand( // middle of enqueue of B. The other thread modifies dependency list of A by // removing C out of it. Iterators become invalid. bool Result = Cmd->enqueue(EnqueueResult, Blocking, ToCleanUp); - if (Result && Cmd->isBlocking() && Cmd != RootCommand) { - Cmd->addBlockedUser(RootCommand->getEvent()); - EnqueueResult = EnqueueResultT(EnqueueResultT::SyclEnqueueBlocked, Cmd); - return false; - } + if (Result) + Result = handleBlockingCmd(Cmd, EnqueueResult, RootCommand, Blocking); return Result; } diff --git a/sycl/source/detail/scheduler/scheduler.hpp b/sycl/source/detail/scheduler/scheduler.hpp index b5c20fbec335c..ed1f8cc922d4b 100644 --- a/sycl/source/detail/scheduler/scheduler.hpp +++ b/sycl/source/detail/scheduler/scheduler.hpp @@ -758,6 +758,9 @@ class Scheduler { std::vector &ToCleanUp, Command *RootCommand, BlockingT Blocking = NON_BLOCKING); + + static bool handleBlockingCmd(Command *Cmd, EnqueueResultT &EnqueueResult, + Command *RootCommand, BlockingT Blocking); }; /// This function waits on all of the graph leaves which somehow use the diff --git a/sycl/unittests/queue/Wait.cpp b/sycl/unittests/queue/Wait.cpp index e1b8eac1c3ee8..3f869900309c1 100644 --- a/sycl/unittests/queue/Wait.cpp +++ b/sycl/unittests/queue/Wait.cpp @@ -133,21 +133,25 @@ TEST(QueueWait, QueueWaitTest) { { TestContext = {}; buffer buf{range<1>(1)}; + + std::mutex m; + std::unique_lock TestLock(m, std::defer_lock); + TestLock.lock(); + event HostTaskEvent = Q.submit([&](handler &Cgh) { auto acc = buf.template get_access(Cgh); - Cgh.host_task([=]() { (void)acc; }); + Cgh.host_task([=, &m]() { + (void)acc; + std::unique_lock InsideHostTaskLock(m); + }); }); std::shared_ptr HostTaskEventImpl = detail::getSyclObjImpl(HostTaskEvent); auto *Cmd = static_cast(HostTaskEventImpl->getCommand()); - detail::Command *EmptyTask = *Cmd->MUsers.begin(); - ASSERT_EQ(EmptyTask->getType(), detail::Command::EMPTY_TASK); - HostTaskEvent.wait(); - // Use the empty task produced by the host task to block the next commands - while (EmptyTask->MEnqueueStatus != - detail::EnqueueResultT::SyclEnqueueSuccess) - continue; - EmptyTask->MEnqueueStatus = detail::EnqueueResultT::SyclEnqueueBlocked; + EXPECT_EQ(Cmd->MUsers.size(), 0u); + EXPECT_TRUE(Cmd->isHostTask()); + + // Use the host task to block the next commands Q.submit([&](handler &Cgh) { auto acc = buf.template get_access(Cgh); Cgh.fill(acc, 42); @@ -156,9 +160,9 @@ TEST(QueueWait, QueueWaitTest) { auto acc = buf.template get_access(Cgh); Cgh.fill(acc, 42); }); - // Unblock the empty task to allow the submitted events to complete once + // Unblock the host task to allow the submitted events to complete once // enqueued. - EmptyTask->MEnqueueStatus = detail::EnqueueResultT::SyclEnqueueSuccess; + TestLock.unlock(); Q.wait(); // Only a single event (the last one) should be waited for here. ASSERT_EQ(TestContext.NEventsWaitedFor, 1); From 9c05886b9429a4288eb48c44fdfcb4e5439f5fd8 Mon Sep 17 00:00:00 2001 From: "Tikhomirova, Kseniya" Date: Tue, 11 Oct 2022 06:24:16 -0700 Subject: [PATCH 06/22] Fix build Signed-off-by: Tikhomirova, Kseniya --- sycl/source/detail/scheduler/graph_processor.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/sycl/source/detail/scheduler/graph_processor.cpp b/sycl/source/detail/scheduler/graph_processor.cpp index 46575299032e9..8593d60f6487d 100644 --- a/sycl/source/detail/scheduler/graph_processor.cpp +++ b/sycl/source/detail/scheduler/graph_processor.cpp @@ -52,13 +52,12 @@ bool Scheduler::GraphProcessor::handleBlockingCmd(Command *Cmd, BlockingT Blocking) { if (Cmd == RootCommand || !Cmd->isBlocking() || Blocking) return true; - if (!Blocking) { - const EventImplPtr &RootCmdEvent = RootCommand->getEvent(); - if (!Cmd->containsBlockedUser(RootCmdEvent)) - Cmd->addBlockedUser(RootCmdEvent); - EnqueueResult = EnqueueResultT(EnqueueResultT::SyclEnqueueBlocked, Cmd); - return false; - } + + const EventImplPtr &RootCmdEvent = RootCommand->getEvent(); + if (!Cmd->containsBlockedUser(RootCmdEvent)) + Cmd->addBlockedUser(RootCmdEvent); + EnqueueResult = EnqueueResultT(EnqueueResultT::SyclEnqueueBlocked, Cmd); + return false; } bool Scheduler::GraphProcessor::enqueueCommand( From 30d46b5d7ab78b96b1e168352d6d34818f00506e Mon Sep 17 00:00:00 2001 From: "Tikhomirova, Kseniya" Date: Fri, 21 Oct 2022 07:03:13 -0700 Subject: [PATCH 07/22] Remove duplicated enqueue Signed-off-by: Tikhomirova, Kseniya --- sycl/source/detail/scheduler/commands.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/sycl/source/detail/scheduler/commands.cpp b/sycl/source/detail/scheduler/commands.cpp index 5e19b80038894..7f02523da3fad 100644 --- a/sycl/source/detail/scheduler/commands.cpp +++ b/sycl/source/detail/scheduler/commands.cpp @@ -341,9 +341,6 @@ class DispatchHostTask { MThisCmd->MEvent->setComplete(); Scheduler::enqueueUnblockedCommands(CmdsToEnqueue, ToCleanUp); - - for (const DepDesc &Dep : Deps) - Scheduler::enqueueLeavesOfReqUnlocked(Dep.MDepRequirement, ToCleanUp); } Sched.cleanupCommands(ToCleanUp); } From 65e97c96b0fdae6815694f0b06fa0232daa48e58 Mon Sep 17 00:00:00 2001 From: "Tikhomirova, Kseniya" Date: Thu, 27 Oct 2022 06:47:03 -0700 Subject: [PATCH 08/22] Fix data races for BlockedUsers Signed-off-by: Tikhomirova, Kseniya --- sycl/source/detail/scheduler/commands.cpp | 13 ++++----- sycl/source/detail/scheduler/commands.hpp | 29 ++++++++----------- .../detail/scheduler/graph_processor.cpp | 18 +++++++----- 3 files changed, 29 insertions(+), 31 deletions(-) diff --git a/sycl/source/detail/scheduler/commands.cpp b/sycl/source/detail/scheduler/commands.cpp index 7f02523da3fad..1317a4a487116 100644 --- a/sycl/source/detail/scheduler/commands.cpp +++ b/sycl/source/detail/scheduler/commands.cpp @@ -334,13 +334,12 @@ class DispatchHostTask { std::vector Deps = MThisCmd->MDeps; - // update self-event status - const std::vector &CmdsToEnqueue = - MThisCmd->getBlockedUsers(); - - MThisCmd->MEvent->setComplete(); - - Scheduler::enqueueUnblockedCommands(CmdsToEnqueue, ToCleanUp); + { + std::lock_guard Guard(MThisCmd->MBlockedUsersMutex); + // update self-event status + MThisCmd->MEvent->setComplete(); + } + Scheduler::enqueueUnblockedCommands(MThisCmd->MBlockedUsers, ToCleanUp); } Sched.cleanupCommands(ToCleanUp); } diff --git a/sycl/source/detail/scheduler/commands.hpp b/sycl/source/detail/scheduler/commands.hpp index a8b353cdbb685..e283a5e55652e 100644 --- a/sycl/source/detail/scheduler/commands.hpp +++ b/sycl/source/detail/scheduler/commands.hpp @@ -151,19 +151,13 @@ class Command { // commands depending on it. Regular usage - host task. bool isBlocking() const { return isHostTask() && !MEvent->isComplete(); } - void addBlockedUser(const EventImplPtr &NewUser) { + void addBlockedUserUnique(const EventImplPtr &NewUser) { + if (std::find(MBlockedUsers.begin(), MBlockedUsers.end(), NewUser) != + MBlockedUsers.end()) + return; MBlockedUsers.push_back(NewUser); } - bool containsBlockedUser(const EventImplPtr &User) const { - return std::find(MBlockedUsers.begin(), MBlockedUsers.end(), User) != - MBlockedUsers.end(); - } - - const std::vector &getBlockedUsers() const { - return MBlockedUsers; - } - const QueueImplPtr &getQueue() const { return MQueue; } const QueueImplPtr &getSubmittedQueue() const { return MSubmittedQueue; } @@ -273,13 +267,6 @@ class Command { friend class DispatchHostTask; - /// Contains list of commands that depends on the host command explicitly (by - /// depends_on). Not involved into cleanup process since it is one-way link - /// and not holds resources. - /// Using EventImplPtr since enqueueUnblockedCommands and event.wait may - /// intersect with command enqueue. - std::vector MBlockedUsers; - public: const std::vector &getPreparedHostDepsEvents() const { return MPreparedHostDepsEvents; @@ -353,6 +340,14 @@ class Command { /// Indicates that the node will be freed by cleanup after enqueue. Such nodes /// should be ignored by other cleanup mechanisms. bool MPostEnqueueCleanup = false; + + /// Contains list of commands that depends on the host command explicitly (by + /// depends_on). Not involved into cleanup process since it is one-way link + /// and not holds resources. + /// Using EventImplPtr since enqueueUnblockedCommands and event.wait may + /// intersect with command enqueue. + std::vector MBlockedUsers; + std::mutex MBlockedUsersMutex; }; /// The empty command does nothing during enqueue. The task can be used to diff --git a/sycl/source/detail/scheduler/graph_processor.cpp b/sycl/source/detail/scheduler/graph_processor.cpp index 8593d60f6487d..9af96beceb720 100644 --- a/sycl/source/detail/scheduler/graph_processor.cpp +++ b/sycl/source/detail/scheduler/graph_processor.cpp @@ -50,14 +50,18 @@ bool Scheduler::GraphProcessor::handleBlockingCmd(Command *Cmd, EnqueueResultT &EnqueueResult, Command *RootCommand, BlockingT Blocking) { - if (Cmd == RootCommand || !Cmd->isBlocking() || Blocking) + if (Cmd == RootCommand || Blocking) return true; - - const EventImplPtr &RootCmdEvent = RootCommand->getEvent(); - if (!Cmd->containsBlockedUser(RootCmdEvent)) - Cmd->addBlockedUser(RootCmdEvent); - EnqueueResult = EnqueueResultT(EnqueueResultT::SyclEnqueueBlocked, Cmd); - return false; + { + std::lock_guard Guard(Cmd->MBlockedUsersMutex); + if (Cmd->isBlocking()) { + const EventImplPtr &RootCmdEvent = RootCommand->getEvent(); + Cmd->addBlockedUserUnique(RootCmdEvent); + EnqueueResult = EnqueueResultT(EnqueueResultT::SyclEnqueueBlocked, Cmd); + return false; + } + } + return true; } bool Scheduler::GraphProcessor::enqueueCommand( From 4871246264c6f93fe1649651fd20a0707a03d3db Mon Sep 17 00:00:00 2001 From: "Tikhomirova, Kseniya" Date: Thu, 27 Oct 2022 07:17:40 -0700 Subject: [PATCH 09/22] Fix event isCompleted impl Signed-off-by: Tikhomirova, Kseniya --- sycl/source/detail/event_impl.cpp | 5 +++++ sycl/source/detail/event_impl.hpp | 2 +- sycl/source/detail/scheduler/commands.hpp | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/sycl/source/detail/event_impl.cpp b/sycl/source/detail/event_impl.cpp index 6f8bd84337c27..26239ac25c613 100644 --- a/sycl/source/detail/event_impl.cpp +++ b/sycl/source/detail/event_impl.cpp @@ -444,6 +444,11 @@ void event_impl::cleanDepEventsThroughOneLevel() { } } +bool event_impl::isCompleted() { + return get_info() == + info::event_command_status::complete; +} + } // namespace detail } // __SYCL_INLINE_VER_NAMESPACE(_V1) } // namespace sycl diff --git a/sycl/source/detail/event_impl.hpp b/sycl/source/detail/event_impl.hpp index 81a018da51a71..deca48d44b43a 100644 --- a/sycl/source/detail/event_impl.hpp +++ b/sycl/source/detail/event_impl.hpp @@ -237,7 +237,7 @@ class event_impl { /// Checks if this event is complete. /// /// \return true if this event is complete. - bool isComplete() const { return MState == HES_Complete; } + bool isCompleted(); private: // When instrumentation is enabled emits trace event for event wait begin and diff --git a/sycl/source/detail/scheduler/commands.hpp b/sycl/source/detail/scheduler/commands.hpp index e283a5e55652e..5aeb832680075 100644 --- a/sycl/source/detail/scheduler/commands.hpp +++ b/sycl/source/detail/scheduler/commands.hpp @@ -149,7 +149,7 @@ class Command { } // Shows thst command could be enqueud, but is blocking enqueue of all // commands depending on it. Regular usage - host task. - bool isBlocking() const { return isHostTask() && !MEvent->isComplete(); } + bool isBlocking() const { return isHostTask() && !MEvent->isCompleted(); } void addBlockedUserUnique(const EventImplPtr &NewUser) { if (std::find(MBlockedUsers.begin(), MBlockedUsers.end(), NewUser) != From b128d7bffa57123df75196be110ab8dcfaab05d8 Mon Sep 17 00:00:00 2001 From: "Tikhomirova, Kseniya" Date: Thu, 17 Nov 2022 06:25:27 -0800 Subject: [PATCH 10/22] DRAFT Signed-off-by: Tikhomirova, Kseniya --- sycl/source/CMakeLists.txt | 2 +- sycl/source/detail/scheduler/commands.cpp | 74 +++++---- sycl/source/detail/scheduler/commands.hpp | 22 ++- .../source/detail/scheduler/graph_builder.cpp | 55 +------ .../detail/scheduler/graph_processor.cpp | 6 - sycl/source/detail/scheduler/scheduler.cpp | 2 +- sycl/source/detail/scheduler/scheduler.hpp | 9 -- sycl/unittests/scheduler/BlockedCommands.cpp | 67 +++----- .../scheduler/EnqueueWithDependsOnDeps.cpp | 12 +- .../scheduler/PostEnqueueCleanup.cpp | 144 +++++++++--------- .../scheduler/SchedulerTestUtils.hpp | 9 -- 11 files changed, 154 insertions(+), 248 deletions(-) diff --git a/sycl/source/CMakeLists.txt b/sycl/source/CMakeLists.txt index 979cf2880f22c..2aa8b76bf2d0e 100644 --- a/sycl/source/CMakeLists.txt +++ b/sycl/source/CMakeLists.txt @@ -3,7 +3,7 @@ #2. Use AddLLVM to modify the build and access config options #cmake_policy(SET CMP0057 NEW) #include(AddLLVM) - +add_definitions(-gdwarf-4 -O0) configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/version.rc.in ${CMAKE_CURRENT_BINARY_DIR}/version.rc diff --git a/sycl/source/detail/scheduler/commands.cpp b/sycl/source/detail/scheduler/commands.cpp index 3e25a13b6b12f..f0f5887ae448d 100644 --- a/sycl/source/detail/scheduler/commands.cpp +++ b/sycl/source/detail/scheduler/commands.cpp @@ -232,6 +232,22 @@ bool Command::isHostTask() const { CG::CGTYPE::CodeplayHostTask); } +bool Command::blockManually(const BlockReason& Reason) { + if (MIsManuallyBlocked) + return false; + MIsManuallyBlocked = true; + MBlockReason = Reason; + return true; +} + +bool Command::unblock() +{ + if (!MIsManuallyBlocked) + return false; + MIsManuallyBlocked = false; + return true; +} + static void flushCrossQueueDeps(const std::vector &EventImpls, const QueueImplPtr &Queue) { for (auto &EventImpl : EventImpls) { @@ -689,35 +705,35 @@ bool Command::enqueue(EnqueueResultT &EnqueueResult, BlockingT Blocking, return true; // If the command is blocked from enqueueing - if (MIsBlockable && MEnqueueStatus == EnqueueResultT::SyclEnqueueBlocked) { - // Exit if enqueue type is not blocking - if (!Blocking) { - EnqueueResult = EnqueueResultT(EnqueueResultT::SyclEnqueueBlocked, this); - return false; - } - static bool ThrowOnBlock = getenv("SYCL_THROW_ON_BLOCK") != nullptr; - if (ThrowOnBlock) - throw sycl::runtime_error( - std::string("Waiting for blocked command. Block reason: ") + - std::string(getBlockReason()), - PI_ERROR_INVALID_OPERATION); - -#ifdef XPTI_ENABLE_INSTRUMENTATION - // Scoped trace event notifier that emits a barrier begin and barrier end - // event, which models the barrier while enqueuing along with the blocked - // reason, as determined by the scheduler - std::string Info = "enqueue.barrier["; - Info += std::string(getBlockReason()) + "]"; - emitInstrumentation(xpti::trace_barrier_begin, Info.c_str()); -#endif - - // Wait if blocking - while (MEnqueueStatus == EnqueueResultT::SyclEnqueueBlocked) - ; -#ifdef XPTI_ENABLE_INSTRUMENTATION - emitInstrumentation(xpti::trace_barrier_end, Info.c_str()); -#endif - } +// if (MIsManuallyBlockable && MEnqueueStatus == EnqueueResultT::SyclEnqueueBlocked) { +// // Exit if enqueue type is not blocking +// if (!Blocking) { +// EnqueueResult = EnqueueResultT(EnqueueResultT::SyclEnqueueBlocked, this); +// return false; +// } +// static bool ThrowOnBlock = getenv("SYCL_THROW_ON_BLOCK") != nullptr; +// if (ThrowOnBlock) +// throw sycl::runtime_error( +// std::string("Waiting for blocked command. Block reason: ") + +// std::string(getBlockReason()), +// PI_ERROR_INVALID_OPERATION); + +// #ifdef XPTI_ENABLE_INSTRUMENTATION +// // Scoped trace event notifier that emits a barrier begin and barrier end +// // event, which models the barrier while enqueuing along with the blocked +// // reason, as determined by the scheduler +// std::string Info = "enqueue.barrier["; +// Info += std::string(getBlockReason()) + "]"; +// emitInstrumentation(xpti::trace_barrier_begin, Info.c_str()); +// #endif + +// // Wait if blocking +// while (MEnqueueStatus == EnqueueResultT::SyclEnqueueBlocked) +// ; +// #ifdef XPTI_ENABLE_INSTRUMENTATION +// emitInstrumentation(xpti::trace_barrier_end, Info.c_str()); +// #endif +// } std::lock_guard Lock(MEnqueueMtx); diff --git a/sycl/source/detail/scheduler/commands.hpp b/sycl/source/detail/scheduler/commands.hpp index 0fc2e0b129138..9753fa9a278a5 100644 --- a/sycl/source/detail/scheduler/commands.hpp +++ b/sycl/source/detail/scheduler/commands.hpp @@ -142,14 +142,12 @@ class Command { return MEnqueueStatus == EnqueueResultT::SyclEnqueueSuccess; } - // Shows that command could not be enqueued, now it may be true for empty task - // only - bool isEnqueueBlocked() const { - return MIsBlockable && MEnqueueStatus == EnqueueResultT::SyclEnqueueBlocked; - } - // Shows thst command could be enqueud, but is blocking enqueue of all - // commands depending on it. Regular usage - host task. - bool isBlocking() const { return isHostTask() && !MEvent->isCompleted(); } + // Shows that command could be enqueued, but blocks enqueue of all + // commands depending on it. Regular usage - host task & host accessors. + bool isBlocking() const { return MIsManuallyBlocked || (isHostTask() && !MEvent->isCompleted()); } + enum class BlockReason : int { HostAccessor = 0, HostTask }; + bool blockManually(const BlockReason& Reason); + bool unblock(); void addBlockedUserUnique(const EventImplPtr &NewUser) { if (std::find(MBlockedUsers.begin(), MBlockedUsers.end(), NewUser) != @@ -277,8 +275,8 @@ class Command { std::vector MDeps; /// Contains list of commands that depend on the command. std::unordered_set MUsers; - /// Indicates whether the command can be blocked from enqueueing. - bool MIsBlockable = false; + /// Indicates whether the command is set as blocking for its users. + bool MIsManuallyBlocked = false; /// Counts the number of memory objects this command is a leaf for. unsigned MLeafCounter = 0; @@ -291,9 +289,7 @@ class Command { /// Used for marking the node during graph traversal. Marks MMarks; - enum class BlockReason : int { HostAccessor = 0, HostTask }; - - // Only have reasonable value while MIsBlockable is true + // Only have reasonable value while MIsManuallyBlocked is true BlockReason MBlockReason; /// Describes the status of the command. diff --git a/sycl/source/detail/scheduler/graph_builder.cpp b/sycl/source/detail/scheduler/graph_builder.cpp index b71defe8a3afb..23ccafd3b4832 100644 --- a/sycl/source/detail/scheduler/graph_builder.cpp +++ b/sycl/source/detail/scheduler/graph_builder.cpp @@ -524,13 +524,9 @@ Scheduler::GraphBuilder::addHostAccessor(Requirement *Req, Command *UpdateHostAccCmd = insertUpdateHostReqCmd(Record, Req, HostQueue, ToEnqueue); + assert(UpdateHostAccCmd->blockManually(Command::BlockReason::HostAccessor)); - // Need empty command to be blocked until host accessor is destructed - EmptyCommand *EmptyCmd = - addEmptyCmd(UpdateHostAccCmd, {Req}, HostQueue, - Command::BlockReason::HostAccessor, ToEnqueue); - - Req->MBlockedCmd = EmptyCmd; + Req->MBlockedCmd = UpdateHostAccCmd; if (MPrintOptionsArray[AfterAddHostAcc]) printGraphAsDot("after_addHostAccessor"); @@ -840,53 +836,6 @@ void Scheduler::GraphBuilder::markModifiedIfWrite(MemObjRecord *Record, } } -template -typename detail::enable_if_t< - std::is_same, Requirement>::value, - EmptyCommand *> -Scheduler::GraphBuilder::addEmptyCmd(Command *Cmd, const std::vector &Reqs, - const QueueImplPtr &Queue, - Command::BlockReason Reason, - std::vector &ToEnqueue, - const bool AddDepsToLeaves) { - EmptyCommand *EmptyCmd = - new EmptyCommand(Scheduler::getInstance().getDefaultHostQueue()); - - if (!EmptyCmd) - throw runtime_error("Out of host memory", PI_ERROR_OUT_OF_HOST_MEMORY); - - EmptyCmd->MIsBlockable = true; - EmptyCmd->MEnqueueStatus = EnqueueResultT::SyclEnqueueBlocked; - EmptyCmd->MBlockReason = Reason; - - for (T *Req : Reqs) { - MemObjRecord *Record = getOrInsertMemObjRecord(Queue, Req, ToEnqueue); - AllocaCommandBase *AllocaCmd = - getOrCreateAllocaForReq(Record, Req, Queue, ToEnqueue); - EmptyCmd->addRequirement(Cmd, AllocaCmd, Req); - } - // addRequirement above call addDep that already will add EmptyCmd as user for - // Cmd no Reqs size check here so assume it is possible to have no Reqs passed - if (!Reqs.size()) - Cmd->addUser(EmptyCmd); - - if (AddDepsToLeaves) { - const std::vector &Deps = Cmd->MDeps; - std::vector ToCleanUp; - for (const DepDesc &Dep : Deps) { - const Requirement *Req = Dep.MDepRequirement; - MemObjRecord *Record = getMemObjRecord(Req->MSYCLMemObj); - - updateLeaves({Cmd}, Record, Req->MAccessMode, ToCleanUp); - addNodeToLeaves(Record, EmptyCmd, Req->MAccessMode, ToEnqueue); - } - for (Command *Cmd : ToCleanUp) - cleanupCommand(Cmd); - } - - return EmptyCmd; -} - static bool isInteropHostTask(const std::unique_ptr &Cmd) { if (Cmd->getCG().getType() != CG::CGTYPE::CodeplayHostTask) return false; diff --git a/sycl/source/detail/scheduler/graph_processor.cpp b/sycl/source/detail/scheduler/graph_processor.cpp index 8849ad8d5f3a4..03b2461530e6e 100644 --- a/sycl/source/detail/scheduler/graph_processor.cpp +++ b/sycl/source/detail/scheduler/graph_processor.cpp @@ -73,12 +73,6 @@ bool Scheduler::GraphProcessor::enqueueCommand( if (Cmd->isSuccessfullyEnqueued()) return handleBlockingCmd(Cmd, EnqueueResult, RootCommand, Blocking); - // Exit early if the command is blocked and the enqueue type is non-blocking - if (Cmd->isEnqueueBlocked() && !Blocking) { - EnqueueResult = EnqueueResultT(EnqueueResultT::SyclEnqueueBlocked, Cmd); - return false; - } - // Recursively enqueue all the implicit + explicit backend level dependencies // first and exit immediately if any of the commands cannot be enqueued. for (const EventImplPtr &Event : Cmd->getPreparedDepsEvents()) { diff --git a/sycl/source/detail/scheduler/scheduler.cpp b/sycl/source/detail/scheduler/scheduler.cpp index 87ca4f7a06902..526377422cee6 100644 --- a/sycl/source/detail/scheduler/scheduler.cpp +++ b/sycl/source/detail/scheduler/scheduler.cpp @@ -344,7 +344,7 @@ void Scheduler::releaseHostAccessor(Requirement *Req) { assert(BlockedCmd && "Can't find appropriate command to unblock"); - BlockedCmd->MEnqueueStatus = EnqueueResultT::SyclEnqueueReady; + BlockedCmd->unblock(); enqueueLeavesOfReqUnlocked(Req, ToCleanUp); } diff --git a/sycl/source/detail/scheduler/scheduler.hpp b/sycl/source/detail/scheduler/scheduler.hpp index 99b65c5c771b1..2ca021e9b9b94 100644 --- a/sycl/source/detail/scheduler/scheduler.hpp +++ b/sycl/source/detail/scheduler/scheduler.hpp @@ -626,15 +626,6 @@ class Scheduler { const Requirement *Req, const ContextImplPtr &Context); - template - typename detail::enable_if_t< - std::is_same, Requirement>::value, - EmptyCommand *> - addEmptyCmd(Command *Cmd, const std::vector &Req, - const QueueImplPtr &Queue, Command::BlockReason Reason, - std::vector &ToEnqueue, - const bool AddDepsToLeaves = true); - protected: /// Finds a command dependency corresponding to the record. DepDesc findDepForRecord(Command *Cmd, MemObjRecord *Record); diff --git a/sycl/unittests/scheduler/BlockedCommands.cpp b/sycl/unittests/scheduler/BlockedCommands.cpp index e5ab988687493..32ad7508598f9 100644 --- a/sycl/unittests/scheduler/BlockedCommands.cpp +++ b/sycl/unittests/scheduler/BlockedCommands.cpp @@ -17,11 +17,14 @@ using namespace testing; TEST_F(SchedulerTest, BlockedCommands) { sycl::unittest::PiMock Mock; sycl::queue Q{Mock.getPlatform().get_devices()[0], MAsyncHandler}; + MockCommand MockCmdBlocking(detail::getSyclObjImpl(Q)); MockCommand MockCmd(detail::getSyclObjImpl(Q)); - MockCmd.MEnqueueStatus = detail::EnqueueResultT::SyclEnqueueBlocked; - MockCmd.MIsBlockable = true; - MockCmd.MRetVal = CL_DEVICE_PARTITION_EQUALLY; + EXPECT_TRUE(MockCmdBlocking.blockManually( + detail::Command::BlockReason::HostAccessor)); + EXPECT_TRUE(MockCmdBlocking.isBlocking()); + std::vector ToCleanUp; + std::ignore = MockCmd.addDep(MockCmdBlocking.getEvent(), ToCleanUp); MockScheduler MS; auto Lock = MS.acquireGraphReadLock(); @@ -32,22 +35,9 @@ TEST_F(SchedulerTest, BlockedCommands) { ASSERT_EQ(detail::EnqueueResultT::SyclEnqueueBlocked, Res.MResult) << "Result of enqueueing blocked command should be BLOCKED\n"; - MockCmd.MEnqueueStatus = detail::EnqueueResultT::SyclEnqueueReady; - Res.MResult = detail::EnqueueResultT::SyclEnqueueSuccess; - MockCmd.MRetVal = CL_DEVICE_PARTITION_EQUALLY; - - Enqueued = MockScheduler::enqueueCommand(&MockCmd, Res, detail::BLOCKING); - ASSERT_FALSE(Enqueued) << "Blocked command should not be enqueued\n"; - ASSERT_EQ(detail::EnqueueResultT::SyclEnqueueFailed, Res.MResult) - << "The command is expected to fail to enqueue.\n"; - ASSERT_EQ(CL_DEVICE_PARTITION_EQUALLY, MockCmd.MRetVal) - << "Expected different error code.\n"; - ASSERT_EQ(&MockCmd, Res.MCmd) << "Expected different failed command.\n"; - Res = detail::EnqueueResultT{}; - MockCmd.MEnqueueStatus = detail::EnqueueResultT::SyclEnqueueReady; - MockCmd.MRetVal = CL_SUCCESS; - Enqueued = MockScheduler::enqueueCommand(&MockCmd, Res, detail::BLOCKING); + MockCmdBlocking.unblock(); + Enqueued = MockScheduler::enqueueCommand(&MockCmd, Res, detail::NON_BLOCKING); ASSERT_TRUE(Enqueued && Res.MResult == detail::EnqueueResultT::SyclEnqueueSuccess) << "The command is expected to be successfully enqueued.\n"; @@ -59,21 +49,18 @@ TEST_F(SchedulerTest, DontEnqueueDepsIfOneOfThemIsBlocked) { MockCommand A(detail::getSyclObjImpl(Q)); A.MEnqueueStatus = detail::EnqueueResultT::SyclEnqueueReady; - A.MIsBlockable = true; A.MRetVal = CL_SUCCESS; MockCommand B(detail::getSyclObjImpl(Q)); B.MEnqueueStatus = detail::EnqueueResultT::SyclEnqueueReady; - B.MIsBlockable = true; B.MRetVal = CL_SUCCESS; MockCommand C(detail::getSyclObjImpl(Q)); - C.MEnqueueStatus = detail::EnqueueResultT::SyclEnqueueBlocked; - C.MIsBlockable = true; + C.MEnqueueStatus = detail::EnqueueResultT::SyclEnqueueReady; + C.blockManually(detail::Command::BlockReason::HostAccessor); MockCommand D(detail::getSyclObjImpl(Q)); D.MEnqueueStatus = detail::EnqueueResultT::SyclEnqueueReady; - D.MIsBlockable = true; D.MRetVal = CL_SUCCESS; addEdge(&A, &B, nullptr); @@ -90,7 +77,7 @@ TEST_F(SchedulerTest, DontEnqueueDepsIfOneOfThemIsBlocked) { EXPECT_CALL(A, enqueue).Times(0); EXPECT_CALL(B, enqueue).Times(1); - EXPECT_CALL(C, enqueue).Times(0); + EXPECT_CALL(C, enqueue).Times(1); EXPECT_CALL(D, enqueue).Times(0); MockScheduler MS; @@ -103,17 +90,16 @@ TEST_F(SchedulerTest, DontEnqueueDepsIfOneOfThemIsBlocked) { ASSERT_EQ(&C, Res.MCmd) << "Expected different failed command.\n"; } -TEST_F(SchedulerTest, EnqueueBlockedCommandEarlyExit) { +TEST_F(SchedulerTest, EnqueueBlockedCommandNoEarlyExit) { sycl::unittest::PiMock Mock; sycl::queue Q{Mock.getPlatform().get_devices()[0], MAsyncHandler}; MockCommand A(detail::getSyclObjImpl(Q)); - A.MEnqueueStatus = detail::EnqueueResultT::SyclEnqueueBlocked; - A.MIsBlockable = true; + A.blockManually(detail::Command::BlockReason::HostAccessor); MockCommand B(detail::getSyclObjImpl(Q)); B.MEnqueueStatus = detail::EnqueueResultT::SyclEnqueueReady; - B.MRetVal = CL_OUT_OF_RESOURCES; + B.MRetVal = CL_SUCCESS; addEdge(&A, &B, nullptr); @@ -121,30 +107,17 @@ TEST_F(SchedulerTest, EnqueueBlockedCommandEarlyExit) { // // A -> B // - // If A is blocked, we should not try to enqueue B. + // If A is blocked, we still should try to enqueue B. - EXPECT_CALL(A, enqueue).Times(0); - EXPECT_CALL(B, enqueue).Times(0); + EXPECT_CALL(A, enqueue).Times(1); + EXPECT_CALL(B, enqueue).Times(1); MockScheduler MS; auto Lock = MS.acquireGraphReadLock(); detail::EnqueueResultT Res; bool Enqueued = MockScheduler::enqueueCommand(&A, Res, detail::NON_BLOCKING); - ASSERT_FALSE(Enqueued) << "Blocked command should not be enqueued\n"; - ASSERT_EQ(detail::EnqueueResultT::SyclEnqueueBlocked, Res.MResult) - << "Result of enqueueing blocked command should be BLOCKED.\n"; - ASSERT_EQ(&A, Res.MCmd) << "Expected different failed command.\n"; - - // But if the enqueue type is blocking we should not exit early. - - EXPECT_CALL(A, enqueue).Times(0); - EXPECT_CALL(B, enqueue).Times(1); - - Enqueued = MockScheduler::enqueueCommand(&A, Res, detail::BLOCKING); - ASSERT_FALSE(Enqueued) << "Blocked command should not be enqueued\n"; - ASSERT_EQ(detail::EnqueueResultT::SyclEnqueueFailed, Res.MResult) - << "Result of enqueueing blocked command should be BLOCKED.\n"; - ASSERT_EQ(&B, Res.MCmd) << "Expected different failed command.\n"; + ASSERT_TRUE(Enqueued) << "Blocking command prevent user from being enqueued " + "but could be enqueued itself\n"; } // This unit test is for workaround described in GraphProcessor::enqueueCommand @@ -155,12 +128,10 @@ TEST_F(SchedulerTest, EnqueueHostDependency) { MockCommand A(detail::getSyclObjImpl(Q)); A.MEnqueueStatus = detail::EnqueueResultT::SyclEnqueueReady; - A.MIsBlockable = true; A.MRetVal = CL_SUCCESS; MockCommand B(detail::getSyclObjImpl(Q)); B.MEnqueueStatus = detail::EnqueueResultT::SyclEnqueueReady; - B.MIsBlockable = true; B.MRetVal = CL_SUCCESS; sycl::detail::EventImplPtr DepEvent{ diff --git a/sycl/unittests/scheduler/EnqueueWithDependsOnDeps.cpp b/sycl/unittests/scheduler/EnqueueWithDependsOnDeps.cpp index 29f99534255f5..bda8ecd8a9df8 100644 --- a/sycl/unittests/scheduler/EnqueueWithDependsOnDeps.cpp +++ b/sycl/unittests/scheduler/EnqueueWithDependsOnDeps.cpp @@ -229,8 +229,7 @@ TEST_F(DependsOnTests, EnqueueNoMemObjDoubleKernelDepHostBlocked) { detail::Command *Cmd1 = AddTaskCG(TestCGType::HOST_TASK, Events); EventImplPtr Cmd1Event = Cmd1->getEvent(); - Cmd1->MIsBlockable = true; - Cmd1->MEnqueueStatus = detail::EnqueueResultT::SyclEnqueueBlocked; + Cmd1->blockManually(detail::Command::BlockReason::HostAccessor); // Depends on host task Events.push_back(Cmd1Event); @@ -254,14 +253,13 @@ TEST_F(DependsOnTests, EnqueueNoMemObjDoubleKernelDepHostBlocked) { EXPECT_EQ(Result.MCmd, static_cast(Cmd1)); // Preconditions for post enqueue checks - EXPECT_FALSE(Cmd1->isSuccessfullyEnqueued()); + EXPECT_TRUE(Cmd1->isSuccessfullyEnqueued()); EXPECT_FALSE(Cmd2->isSuccessfullyEnqueued()); EXPECT_FALSE(Cmd3->isSuccessfullyEnqueued()); - Cmd1->MEnqueueStatus = detail::EnqueueResultT::SyclEnqueueReady; - - std::vector BlockedCommands{Cmd2, Cmd3}; - VerifyBlockedCommandsEnqueue(Cmd1, BlockedCommands); + Cmd1->unblock(); + EXPECT_TRUE(MS.enqueueCommand(Cmd2, Result, detail::BlockingT::NON_BLOCKING)); + EXPECT_TRUE(MS.enqueueCommand(Cmd3, Result, detail::BlockingT::NON_BLOCKING)); } TEST_F(DependsOnTests, EnqueueNoMemObjDoubleKernelDepHost) { diff --git a/sycl/unittests/scheduler/PostEnqueueCleanup.cpp b/sycl/unittests/scheduler/PostEnqueueCleanup.cpp index c0c1869ad318b..d3581e1f3a55c 100644 --- a/sycl/unittests/scheduler/PostEnqueueCleanup.cpp +++ b/sycl/unittests/scheduler/PostEnqueueCleanup.cpp @@ -198,79 +198,79 @@ static void checkCleanupOnLeafUpdate( MS.removeRecordForMemObj(detail::getSyclObjImpl(Buf).get()); } -TEST_F(SchedulerTest, PostEnqueueCleanup) { - // Enforce creation of linked commands to test all sites of calling cleanup. - unittest::ScopedEnvVar HostUnifiedMemoryVar{ - HostUnifiedMemoryName, "1", - detail::SYCLConfig::reset}; - sycl::unittest::PiMock Mock; - sycl::platform Plt = Mock.getPlatform(); - Mock.redefineBefore( - redefinedEnqueueMemBufferMap); - Mock.redefineBefore( - redefinedEnqueueMemUnmap); - Mock.redefineBefore( - redefinedEnqueueMemBufferFill); +// TEST_F(SchedulerTest, PostEnqueueCleanup) { +// // Enforce creation of linked commands to test all sites of calling cleanup. +// unittest::ScopedEnvVar HostUnifiedMemoryVar{ +// HostUnifiedMemoryName, "1", +// detail::SYCLConfig::reset}; +// sycl::unittest::PiMock Mock; +// sycl::platform Plt = Mock.getPlatform(); +// Mock.redefineBefore( +// redefinedEnqueueMemBufferMap); +// Mock.redefineBefore( +// redefinedEnqueueMemUnmap); +// Mock.redefineBefore( +// redefinedEnqueueMemBufferFill); - context Ctx{Plt}; - queue Queue{Ctx, default_selector_v}; - detail::QueueImplPtr QueueImpl = detail::getSyclObjImpl(Queue); - MockScheduler MS; +// context Ctx{Plt}; +// queue Queue{Ctx, default_selector_v}; +// detail::QueueImplPtr QueueImpl = detail::getSyclObjImpl(Queue); +// MockScheduler MS; - buffer Buf{range<1>(1)}; - std::shared_ptr BufImpl = detail::getSyclObjImpl(Buf); - detail::Requirement MockReq = getMockRequirement(Buf); - MockReq.MDims = 1; - MockReq.MSYCLMemObj = BufImpl.get(); +// buffer Buf{range<1>(1)}; +// std::shared_ptr BufImpl = detail::getSyclObjImpl(Buf); +// detail::Requirement MockReq = getMockRequirement(Buf); +// MockReq.MDims = 1; +// MockReq.MSYCLMemObj = BufImpl.get(); - checkCleanupOnEnqueue(MS, QueueImpl, Buf, MockReq); - std::vector ToEnqueue; - checkCleanupOnLeafUpdate(MS, QueueImpl, Buf, MockReq, - [&](detail::MemObjRecord *Record) { - MS.decrementLeafCountersForRecord(Record); - }); - checkCleanupOnLeafUpdate( - MS, QueueImpl, Buf, MockReq, [&](detail::MemObjRecord *Record) { - MS.insertMemoryMove(Record, &MockReq, QueueImpl, ToEnqueue); - }); - checkCleanupOnLeafUpdate(MS, QueueImpl, Buf, MockReq, - [&](detail::MemObjRecord *Record) { - Record->MMemModified = true; - MS.addCopyBack(&MockReq, ToEnqueue); - }); - checkCleanupOnLeafUpdate( - MS, QueueImpl, Buf, MockReq, [&](detail::MemObjRecord *Record) { - detail::Command *Leaf = *Record->MWriteLeaves.begin(); - MS.addEmptyCmd(Leaf, {&MockReq}, QueueImpl, - detail::Command::BlockReason::HostTask, ToEnqueue); - }); - device HostDevice = detail::createSyclObjFromImpl( - detail::device_impl::getHostDeviceImpl()); - detail::QueueImplPtr DefaultHostQueue{ - new detail::queue_impl(detail::getSyclObjImpl(HostDevice), {}, {})}; - checkCleanupOnLeafUpdate( - MS, DefaultHostQueue, Buf, MockReq, [&](detail::MemObjRecord *Record) { - MS.getOrCreateAllocaForReq(Record, &MockReq, QueueImpl, ToEnqueue); - }); - // Check cleanup on exceeding leaf limit. - checkCleanupOnLeafUpdate( - MS, QueueImpl, Buf, MockReq, [&](detail::MemObjRecord *Record) { - std::vector> Leaves; - for (std::size_t I = 0; - I < Record->MWriteLeaves.genericCommandsCapacity(); ++I) - Leaves.push_back(std::make_unique(QueueImpl, MockReq)); +// checkCleanupOnEnqueue(MS, QueueImpl, Buf, MockReq); +// std::vector ToEnqueue; +// checkCleanupOnLeafUpdate(MS, QueueImpl, Buf, MockReq, +// [&](detail::MemObjRecord *Record) { +// MS.decrementLeafCountersForRecord(Record); +// }); +// checkCleanupOnLeafUpdate( +// MS, QueueImpl, Buf, MockReq, [&](detail::MemObjRecord *Record) { +// MS.insertMemoryMove(Record, &MockReq, QueueImpl, ToEnqueue); +// }); +// checkCleanupOnLeafUpdate(MS, QueueImpl, Buf, MockReq, +// [&](detail::MemObjRecord *Record) { +// Record->MMemModified = true; +// MS.addCopyBack(&MockReq, ToEnqueue); +// }); +// checkCleanupOnLeafUpdate( +// MS, QueueImpl, Buf, MockReq, [&](detail::MemObjRecord *Record) { +// detail::Command *Leaf = *Record->MWriteLeaves.begin(); +// MS.addEmptyCmd(Leaf, {&MockReq}, QueueImpl, +// detail::Command::BlockReason::HostTask, ToEnqueue); +// }); +// device HostDevice = detail::createSyclObjFromImpl( +// detail::device_impl::getHostDeviceImpl()); +// detail::QueueImplPtr DefaultHostQueue{ +// new detail::queue_impl(detail::getSyclObjImpl(HostDevice), {}, {})}; +// checkCleanupOnLeafUpdate( +// MS, DefaultHostQueue, Buf, MockReq, [&](detail::MemObjRecord *Record) { +// MS.getOrCreateAllocaForReq(Record, &MockReq, QueueImpl, ToEnqueue); +// }); +// // Check cleanup on exceeding leaf limit. +// checkCleanupOnLeafUpdate( +// MS, QueueImpl, Buf, MockReq, [&](detail::MemObjRecord *Record) { +// std::vector> Leaves; +// for (std::size_t I = 0; +// I < Record->MWriteLeaves.genericCommandsCapacity(); ++I) +// Leaves.push_back(std::make_unique(QueueImpl, MockReq)); - detail::AllocaCommandBase *AllocaCmd = Record->MAllocaCommands[0]; - std::vector ToCleanUp; - for (std::unique_ptr &MockCmd : Leaves) { - (void)MockCmd->addDep(detail::DepDesc(AllocaCmd, &MockReq, AllocaCmd), - ToCleanUp); - MS.addNodeToLeaves(Record, MockCmd.get(), access::mode::read_write, - ToEnqueue); - } - for (std::unique_ptr &MockCmd : Leaves) - MS.updateLeaves({MockCmd.get()}, Record, access::mode::read_write, - ToCleanUp); - EXPECT_TRUE(ToCleanUp.empty()); - }); -} +// detail::AllocaCommandBase *AllocaCmd = Record->MAllocaCommands[0]; +// std::vector ToCleanUp; +// for (std::unique_ptr &MockCmd : Leaves) { +// (void)MockCmd->addDep(detail::DepDesc(AllocaCmd, &MockReq, AllocaCmd), +// ToCleanUp); +// MS.addNodeToLeaves(Record, MockCmd.get(), access::mode::read_write, +// ToEnqueue); +// } +// for (std::unique_ptr &MockCmd : Leaves) +// MS.updateLeaves({MockCmd.get()}, Record, access::mode::read_write, +// ToCleanUp); +// EXPECT_TRUE(ToCleanUp.empty()); +// }); +// } diff --git a/sycl/unittests/scheduler/SchedulerTestUtils.hpp b/sycl/unittests/scheduler/SchedulerTestUtils.hpp index 5f30f5eb67ea2..01f97942a1455 100644 --- a/sycl/unittests/scheduler/SchedulerTestUtils.hpp +++ b/sycl/unittests/scheduler/SchedulerTestUtils.hpp @@ -184,15 +184,6 @@ class MockScheduler : public sycl::detail::Scheduler { return MGraphBuilder.insertUpdateHostReqCmd(Record, Req, Queue, ToEnqueue); } - sycl::detail::EmptyCommand * - addEmptyCmd(sycl::detail::Command *Cmd, - const std::vector &Reqs, - const sycl::detail::QueueImplPtr &Queue, - sycl::detail::Command::BlockReason Reason, - std::vector &ToEnqueue) { - return MGraphBuilder.addEmptyCmd(Cmd, Reqs, Queue, Reason, ToEnqueue); - } - sycl::detail::Command * addCG(std::unique_ptr CommandGroup, sycl::detail::QueueImplPtr Queue, From 64ef595ed23c3306cecdfb8e079ff1e513a12246 Mon Sep 17 00:00:00 2001 From: "Tikhomirova, Kseniya" Date: Tue, 29 Nov 2022 02:22:10 -0800 Subject: [PATCH 11/22] Remove empty task usage from host accessor path Signed-off-by: Tikhomirova, Kseniya --- sycl/source/detail/scheduler/commands.cpp | 153 +++++------------- sycl/source/detail/scheduler/commands.hpp | 33 +--- .../detail/scheduler/graph_processor.cpp | 13 +- .../detail/scheduler/leaves_collection.cpp | 20 ++- .../detail/scheduler/leaves_collection.hpp | 10 +- .../scheduler/EnqueueWithDependsOnDeps.cpp | 2 +- sycl/unittests/scheduler/LeavesCollection.cpp | 16 +- 7 files changed, 79 insertions(+), 168 deletions(-) diff --git a/sycl/source/detail/scheduler/commands.cpp b/sycl/source/detail/scheduler/commands.cpp index f0f5887ae448d..f061e803bb01f 100644 --- a/sycl/source/detail/scheduler/commands.cpp +++ b/sycl/source/detail/scheduler/commands.cpp @@ -166,8 +166,6 @@ static std::string commandToNodeType(Command::CommandType Type) { return "memory_transfer_node"; case Command::CommandType::UPDATE_REQUIREMENT: return "host_acc_create_buffer_lock_node"; - case Command::CommandType::EMPTY_TASK: - return "host_acc_destroy_buffer_release_node"; default: return "unknown_node"; } @@ -194,8 +192,6 @@ static std::string commandToName(Command::CommandType Type) { return "Memory Transfer (Unmap)"; case Command::CommandType::UPDATE_REQUIREMENT: return "Host Accessor Creation/Buffer Lock"; - case Command::CommandType::EMPTY_TASK: - return "Host Accessor Destruction/Buffer Lock Release"; default: return "Unknown Action"; } @@ -232,7 +228,7 @@ bool Command::isHostTask() const { CG::CGTYPE::CodeplayHostTask); } -bool Command::blockManually(const BlockReason& Reason) { +bool Command::blockManually(const BlockReason &Reason) { if (MIsManuallyBlocked) return false; MIsManuallyBlocked = true; @@ -240,8 +236,7 @@ bool Command::blockManually(const BlockReason& Reason) { return true; } -bool Command::unblock() -{ +bool Command::unblock() { if (!MIsManuallyBlocked) return false; MIsManuallyBlocked = false; @@ -643,7 +638,7 @@ Command *Command::addDep(DepDesc NewDep, std::vector &ToCleanUp) { processDepEvent(NewDep.MDepCommand->getEvent(), NewDep, ToCleanUp); } // ConnectionCmd insertion builds the following dependency structure: - // this -> emptyCmd (for ConnectionCmd) -> ConnectionCmd -> NewDep + // this -> ConnectionCmd -> NewDep // that means that this and NewDep are already dependent if (!ConnectionCmd) { MDeps.push_back(NewDep); @@ -705,35 +700,38 @@ bool Command::enqueue(EnqueueResultT &EnqueueResult, BlockingT Blocking, return true; // If the command is blocked from enqueueing -// if (MIsManuallyBlockable && MEnqueueStatus == EnqueueResultT::SyclEnqueueBlocked) { -// // Exit if enqueue type is not blocking -// if (!Blocking) { -// EnqueueResult = EnqueueResultT(EnqueueResultT::SyclEnqueueBlocked, this); -// return false; -// } -// static bool ThrowOnBlock = getenv("SYCL_THROW_ON_BLOCK") != nullptr; -// if (ThrowOnBlock) -// throw sycl::runtime_error( -// std::string("Waiting for blocked command. Block reason: ") + -// std::string(getBlockReason()), -// PI_ERROR_INVALID_OPERATION); - -// #ifdef XPTI_ENABLE_INSTRUMENTATION -// // Scoped trace event notifier that emits a barrier begin and barrier end -// // event, which models the barrier while enqueuing along with the blocked -// // reason, as determined by the scheduler -// std::string Info = "enqueue.barrier["; -// Info += std::string(getBlockReason()) + "]"; -// emitInstrumentation(xpti::trace_barrier_begin, Info.c_str()); -// #endif - -// // Wait if blocking -// while (MEnqueueStatus == EnqueueResultT::SyclEnqueueBlocked) -// ; -// #ifdef XPTI_ENABLE_INSTRUMENTATION -// emitInstrumentation(xpti::trace_barrier_end, Info.c_str()); -// #endif -// } + // if (MIsManuallyBlockable && MEnqueueStatus == + // EnqueueResultT::SyclEnqueueBlocked) { + // // Exit if enqueue type is not blocking + // if (!Blocking) { + // EnqueueResult = EnqueueResultT(EnqueueResultT::SyclEnqueueBlocked, + // this); return false; + // } + // static bool ThrowOnBlock = getenv("SYCL_THROW_ON_BLOCK") != nullptr; + // if (ThrowOnBlock) + // throw sycl::runtime_error( + // std::string("Waiting for blocked command. Block reason: ") + + // std::string(getBlockReason()), + // PI_ERROR_INVALID_OPERATION); + + // #ifdef XPTI_ENABLE_INSTRUMENTATION + // // Scoped trace event notifier that emits a barrier begin and barrier + // end + // // event, which models the barrier while enqueuing along with the + // blocked + // // reason, as determined by the scheduler + // std::string Info = "enqueue.barrier["; + // Info += std::string(getBlockReason()) + "]"; + // emitInstrumentation(xpti::trace_barrier_begin, Info.c_str()); + // #endif + + // // Wait if blocking + // while (MEnqueueStatus == EnqueueResultT::SyclEnqueueBlocked) + // ; + // #ifdef XPTI_ENABLE_INSTRUMENTATION + // emitInstrumentation(xpti::trace_barrier_end, Info.c_str()); + // #endif + // } std::lock_guard Lock(MEnqueueMtx); @@ -1555,83 +1553,6 @@ pi_int32 MemCpyCommandHost::enqueueImp() { return PI_SUCCESS; } -EmptyCommand::EmptyCommand(QueueImplPtr Queue) - : Command(CommandType::EMPTY_TASK, std::move(Queue)) { - emitInstrumentationDataProxy(); -} - -pi_int32 EmptyCommand::enqueueImp() { - waitForPreparedHostEvents(); - waitForEvents(MQueue, MPreparedDepsEvents, MEvent->getHandleRef()); - - return PI_SUCCESS; -} - -void EmptyCommand::addRequirement(Command *DepCmd, AllocaCommandBase *AllocaCmd, - const Requirement *Req) { - const Requirement &ReqRef = *Req; - MRequirements.emplace_back(ReqRef); - const Requirement *const StoredReq = &MRequirements.back(); - - // EmptyCommand is always host one, so we believe that result of addDep is - // nil - std::vector ToCleanUp; - Command *Cmd = addDep(DepDesc{DepCmd, StoredReq, AllocaCmd}, ToCleanUp); - assert(Cmd == nullptr && "Conection command should be null for EmptyCommand"); - assert(ToCleanUp.empty() && "addDep should add a command for cleanup only if " - "there's a connection command"); - (void)Cmd; -} - -void EmptyCommand::emitInstrumentationData() { -#ifdef XPTI_ENABLE_INSTRUMENTATION - if (!xptiTraceEnabled()) - return; - // Create a payload with the command name and an event using this payload to - // emit a node_create - if (MRequirements.empty()) - return; - - Requirement &Req = *MRequirements.begin(); - - MAddress = Req.MSYCLMemObj; - makeTraceEventProlog(MAddress); - - if (MFirstInstance) { - xpti_td *CmdTraceEvent = static_cast(MTraceEvent); - xpti::addMetadata(CmdTraceEvent, "sycl_device", - deviceToID(MQueue->get_device())); - xpti::addMetadata(CmdTraceEvent, "sycl_device_type", - deviceToString(MQueue->get_device())); - xpti::addMetadata(CmdTraceEvent, "sycl_device_name", - getSyclObjImpl(MQueue->get_device())->getDeviceName()); - xpti::addMetadata(CmdTraceEvent, "memory_object", - reinterpret_cast(MAddress)); - makeTraceEventEpilog(); - } -#endif -} - -void EmptyCommand::printDot(std::ostream &Stream) const { - Stream << "\"" << this << "\" [style=filled, fillcolor=\"#8d8f29\", label=\""; - - Stream << "ID = " << this << "\\n"; - Stream << "EMPTY NODE" - << "\\n"; - - Stream << "\"];" << std::endl; - - for (const auto &Dep : MDeps) { - Stream << " \"" << this << "\" -> \"" << Dep.MDepCommand << "\"" - << " [ label = \"Access mode: " - << accessModeToString(Dep.MDepRequirement->MAccessMode) << "\\n" - << "MemObj: " << Dep.MDepRequirement->MSYCLMemObj << " \" ]" - << std::endl; - } -} - -bool EmptyCommand::producesPiEvent() const { return false; } - void MemCpyCommandHost::printDot(std::ostream &Stream) const { Stream << "\"" << this << "\" [style=filled, fillcolor=\"#B6A2EB\", label=\""; @@ -1682,6 +1603,10 @@ void UpdateHostRequirementCommand::emitInstrumentationData() { #endif } +bool UpdateHostRequirementCommand::supportsPostEnqueueCleanup() const { + return !isBlocking(); // to think, may be moved to Command? +} + static std::string cgTypeToString(detail::CG::CGTYPE Type) { switch (Type) { case detail::CG::Kernel: diff --git a/sycl/source/detail/scheduler/commands.hpp b/sycl/source/detail/scheduler/commands.hpp index 9753fa9a278a5..3e6b802bfae05 100644 --- a/sycl/source/detail/scheduler/commands.hpp +++ b/sycl/source/detail/scheduler/commands.hpp @@ -42,7 +42,6 @@ class AllocaCommand; class AllocaCommandBase; class ReleaseCommand; class ExecCGCommand; -class EmptyCommand; enum BlockingT { NON_BLOCKING = 0, BLOCKING }; @@ -103,7 +102,6 @@ class Command { MAP_MEM_OBJ, UNMAP_MEM_OBJ, UPDATE_REQUIREMENT, - EMPTY_TASK, HOST_TASK }; @@ -144,9 +142,11 @@ class Command { // Shows that command could be enqueued, but blocks enqueue of all // commands depending on it. Regular usage - host task & host accessors. - bool isBlocking() const { return MIsManuallyBlocked || (isHostTask() && !MEvent->isCompleted()); } + bool isBlocking() const { + return MIsManuallyBlocked || (isHostTask() && !MEvent->isCompleted()); + } enum class BlockReason : int { HostAccessor = 0, HostTask }; - bool blockManually(const BlockReason& Reason); + bool blockManually(const BlockReason &Reason); bool unblock(); void addBlockedUserUnique(const EventImplPtr &NewUser) { @@ -343,30 +343,6 @@ class Command { std::mutex MBlockedUsersMutex; }; -/// The empty command does nothing during enqueue. The task can be used to -/// implement lock in the graph, or to merge several nodes into one. -class EmptyCommand : public Command { -public: - EmptyCommand(QueueImplPtr Queue); - - void printDot(std::ostream &Stream) const final; - const Requirement *getRequirement() const final { return &MRequirements[0]; } - void addRequirement(Command *DepCmd, AllocaCommandBase *AllocaCmd, - const Requirement *Req); - - void emitInstrumentationData() override; - - bool producesPiEvent() const final; - -private: - pi_int32 enqueueImp() final; - - // Employing deque here as it allows to push_back/emplace_back without - // invalidation of pointer or reference to stored data item regardless of - // iterator invalidation. - std::deque MRequirements; -}; - /// The release command enqueues release of a memory object instance allocated /// on Host or underlying framework. class ReleaseCommand : public Command { @@ -599,6 +575,7 @@ class UpdateHostRequirementCommand : public Command { void printDot(std::ostream &Stream) const final; const Requirement *getRequirement() const final { return &MDstReq; } void emitInstrumentationData() final; + bool supportsPostEnqueueCleanup() const final; private: pi_int32 enqueueImp() final; diff --git a/sycl/source/detail/scheduler/graph_processor.cpp b/sycl/source/detail/scheduler/graph_processor.cpp index 03b2461530e6e..b7135cf9c3484 100644 --- a/sycl/source/detail/scheduler/graph_processor.cpp +++ b/sycl/source/detail/scheduler/graph_processor.cpp @@ -50,11 +50,22 @@ bool Scheduler::GraphProcessor::handleBlockingCmd(Command *Cmd, EnqueueResultT &EnqueueResult, Command *RootCommand, BlockingT Blocking) { - if (Cmd == RootCommand || Blocking) + + static bool ThrowOnBlock = getenv("SYCL_THROW_ON_BLOCK") != nullptr; + // No error to be returned for root command. + // Blocking && !ThrowOnBlock means that we will wait for task in parent command enqueue if it is blocking and do not report it to user. + if ((Cmd == RootCommand) || (Blocking && !ThrowOnBlock)) return true; + { std::lock_guard Guard(Cmd->MBlockedUsersMutex); if (Cmd->isBlocking()) { + if (Blocking && ThrowOnBlock) + // Means that we are going to wait on Blocking command + throw sycl::runtime_error( + std::string("Waiting for blocked command. Block reason: ") + + std::string(Cmd->getBlockReason()), + PI_ERROR_INVALID_OPERATION); const EventImplPtr &RootCmdEvent = RootCommand->getEvent(); Cmd->addBlockedUserUnique(RootCmdEvent); EnqueueResult = EnqueueResultT(EnqueueResultT::SyclEnqueueBlocked, Cmd); diff --git a/sycl/source/detail/scheduler/leaves_collection.cpp b/sycl/source/detail/scheduler/leaves_collection.cpp index 586baf973ff98..a03ccf40b32c6 100644 --- a/sycl/source/detail/scheduler/leaves_collection.cpp +++ b/sycl/source/detail/scheduler/leaves_collection.cpp @@ -31,8 +31,7 @@ static inline bool doOverlap(const Requirement *LHS, const Requirement *RHS) { } static inline bool isHostAccessorCmd(Command *Cmd) { - return Cmd->getType() == Command::EMPTY_TASK && - Cmd->MBlockReason == Command::BlockReason::HostAccessor; + return Cmd->getType() == Command::UPDATE_REQUIREMENT; } size_t LeavesCollection::remove(value_type Cmd) { @@ -46,15 +45,14 @@ size_t LeavesCollection::remove(value_type Cmd) { } // host accessor commands part - return eraseHostAccessorCommand(static_cast(Cmd)); + return eraseHostAccessorCommand(Cmd); } bool LeavesCollection::push_back(value_type Cmd, EnqueueListT &ToEnqueue) { bool Result = false; if (isHostAccessorCmd(Cmd)) - Result = - addHostAccessorCommand(static_cast(Cmd), ToEnqueue); + Result = addHostAccessorCommand(Cmd, ToEnqueue); else Result = addGenericCommand(Cmd, ToEnqueue); @@ -67,13 +65,13 @@ std::vector LeavesCollection::toVector() const { Result.insert(Result.end(), MGenericCommands.begin(), MGenericCommands.end()); - for (EmptyCommand *Cmd : MHostAccessorCommands) + for (Command *Cmd : MHostAccessorCommands) Result.push_back(Cmd); return Result; } -bool LeavesCollection::addHostAccessorCommand(EmptyCommand *Cmd, +bool LeavesCollection::addHostAccessorCommand(Command *Cmd, EnqueueListT &ToEnqueue) { // 1. find the oldest command with doOverlap() = true amongst the List // => OldCmd @@ -86,7 +84,7 @@ bool LeavesCollection::addHostAccessorCommand(EmptyCommand *Cmd, else OldCmdIt = std::find_if( MHostAccessorCommands.begin(), MHostAccessorCommands.end(), - [&](const EmptyCommand *Test) -> bool { + [&](const Command *Test) -> bool { return doOverlap(Test->getRequirement(), Cmd->getRequirement()); }); @@ -102,7 +100,7 @@ bool LeavesCollection::addHostAccessorCommand(EmptyCommand *Cmd, MAllocateDependency(Cmd, *OldCmdIt, MRecord, ToEnqueue); // erase the old cmd as it's tracked via dependency now - eraseHostAccessorCommand(static_cast(*OldCmdIt)); + eraseHostAccessorCommand(*OldCmdIt); } // 2.2 If OldCmd == null: @@ -128,12 +126,12 @@ bool LeavesCollection::addGenericCommand(Command *Cmd, return true; } -void LeavesCollection::insertHostAccessorCommand(EmptyCommand *Cmd) { +void LeavesCollection::insertHostAccessorCommand(Command *Cmd) { MHostAccessorCommandsXRef[Cmd] = MHostAccessorCommands.insert(MHostAccessorCommands.end(), Cmd); } -size_t LeavesCollection::eraseHostAccessorCommand(EmptyCommand *Cmd) { +size_t LeavesCollection::eraseHostAccessorCommand(Command *Cmd) { auto XRefIt = MHostAccessorCommandsXRef.find(Cmd); if (XRefIt == MHostAccessorCommandsXRef.end()) diff --git a/sycl/source/detail/scheduler/leaves_collection.hpp b/sycl/source/detail/scheduler/leaves_collection.hpp index c8f73645fcb06..b770415650501 100644 --- a/sycl/source/detail/scheduler/leaves_collection.hpp +++ b/sycl/source/detail/scheduler/leaves_collection.hpp @@ -38,7 +38,7 @@ struct MemObjRecord; class LeavesCollection { public: using GenericCommandsT = CircularBuffer; - using HostAccessorCommandsT = std::list; + using HostAccessorCommandsT = std::list; using EnqueueListT = std::vector; // Make first command depend on the second @@ -117,7 +117,7 @@ class LeavesCollection { using HostAccessorCommandSingleXRefT = typename HostAccessorCommandsT::iterator; using HostAccessorCommandsXRefT = - std::unordered_map; + std::unordered_map; MemObjRecord *MRecord; GenericCommandsT MGenericCommands; @@ -127,12 +127,12 @@ class LeavesCollection { AllocateDependencyF MAllocateDependency; bool addGenericCommand(value_type Cmd, EnqueueListT &ToEnqueue); - bool addHostAccessorCommand(EmptyCommand *Cmd, EnqueueListT &ToEnqueue); + bool addHostAccessorCommand(Command *Cmd, EnqueueListT &ToEnqueue); // inserts a command to the end of list for its mem object - void insertHostAccessorCommand(EmptyCommand *Cmd); + void insertHostAccessorCommand(Command *Cmd); // returns number of removed elements - size_t eraseHostAccessorCommand(EmptyCommand *Cmd); + size_t eraseHostAccessorCommand(Command *Cmd); typename Iterator::type beginHostAccessor() { return MHostAccessorCommands.begin(); diff --git a/sycl/unittests/scheduler/EnqueueWithDependsOnDeps.cpp b/sycl/unittests/scheduler/EnqueueWithDependsOnDeps.cpp index bda8ecd8a9df8..f111bebd4e663 100644 --- a/sycl/unittests/scheduler/EnqueueWithDependsOnDeps.cpp +++ b/sycl/unittests/scheduler/EnqueueWithDependsOnDeps.cpp @@ -258,7 +258,7 @@ TEST_F(DependsOnTests, EnqueueNoMemObjDoubleKernelDepHostBlocked) { EXPECT_FALSE(Cmd3->isSuccessfullyEnqueued()); Cmd1->unblock(); - EXPECT_TRUE(MS.enqueueCommand(Cmd2, Result, detail::BlockingT::NON_BLOCKING)); + EXPECT_TRUE(MS.enqueueCommand(Cmd2, Result, detail::BlockingT::BLOCKING)); EXPECT_TRUE(MS.enqueueCommand(Cmd3, Result, detail::BlockingT::NON_BLOCKING)); } diff --git a/sycl/unittests/scheduler/LeavesCollection.cpp b/sycl/unittests/scheduler/LeavesCollection.cpp index ea883041add66..29caead28105f 100644 --- a/sycl/unittests/scheduler/LeavesCollection.cpp +++ b/sycl/unittests/scheduler/LeavesCollection.cpp @@ -37,11 +37,11 @@ createGenericCommand(const std::shared_ptr &Q) { } std::shared_ptr -createEmptyCommand(const std::shared_ptr &Q, - const Requirement &Req) { - EmptyCommand *Cmd = new EmptyCommand(Q); - Cmd->addRequirement(/* DepCmd = */ nullptr, /* AllocaCmd = */ nullptr, &Req); - Cmd->MBlockReason = Command::BlockReason::HostAccessor; +createHostAccCommand(const std::shared_ptr &Q, + const Requirement &Req) { + Command *Cmd = new UpdateHostRequirementCommand( + Q, Req, /*SrcAllocaCmd*/ nullptr, /*DstPtr*/ nullptr); + Cmd->blockManually(Command::BlockReason::HostAccessor); return std::shared_ptr{Cmd}; } @@ -83,7 +83,7 @@ TEST_F(LeavesCollectionTest, PushBack) { << "Host accessor commands container isn't empty, but it should be."; } - // add mix of generic and empty commands + // add mix of generic and host acc commands { sycl::buffer Buf(sycl::range<1>(1)); @@ -97,7 +97,7 @@ TEST_F(LeavesCollectionTest, PushBack) { for (size_t Idx = 0; Idx < GenericCmdsCapacity * 4; ++Idx) { auto Cmd = Idx % 2 ? createGenericCommand(getSyclObjImpl(Q)) - : createEmptyCommand(getSyclObjImpl(Q), MockReq); + : createHostAccCommand(getSyclObjImpl(Q), MockReq); Cmds.push_back(Cmd); LE.push_back(Cmds.back().get(), ToEnqueue); @@ -137,7 +137,7 @@ TEST_F(LeavesCollectionTest, Remove) { for (size_t Idx = 0; Idx < GenericCmdsCapacity * 4; ++Idx) { auto Cmd = Idx % 2 ? createGenericCommand(getSyclObjImpl(Q)) - : createEmptyCommand(getSyclObjImpl(Q), MockReq); + : createHostAccCommand(getSyclObjImpl(Q), MockReq); Cmds.push_back(Cmd); if (LE.push_back(Cmds.back().get(), ToEnqueue)) From 1aff2c81f31a533dc28d442a4ea023821fc57f80 Mon Sep 17 00:00:00 2001 From: "Tikhomirova, Kseniya" Date: Fri, 2 Dec 2022 06:17:40 -0800 Subject: [PATCH 12/22] Remove lines uploaded by mistake Signed-off-by: Tikhomirova, Kseniya --- sycl/source/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sycl/source/CMakeLists.txt b/sycl/source/CMakeLists.txt index 537400f956af8..c61b10da7f195 100644 --- a/sycl/source/CMakeLists.txt +++ b/sycl/source/CMakeLists.txt @@ -3,7 +3,7 @@ #2. Use AddLLVM to modify the build and access config options #cmake_policy(SET CMP0057 NEW) #include(AddLLVM) -add_definitions(-gdwarf-4 -O0) + configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/version.rc.in ${CMAKE_CURRENT_BINARY_DIR}/version.rc From 4d389ece788cbf2245f9e46ecafbd7899e3e1ad1 Mon Sep 17 00:00:00 2001 From: "Tikhomirova, Kseniya" Date: Fri, 2 Dec 2022 08:04:15 -0800 Subject: [PATCH 13/22] draft to amend when sycl_throw_on_block deleted Signed-off-by: Tikhomirova, Kseniya --- sycl/source/detail/scheduler/commands.cpp | 21 ++---------- sycl/source/detail/scheduler/commands.hpp | 3 +- .../detail/scheduler/graph_processor.cpp | 32 +++++++++++++++++++ 3 files changed, 37 insertions(+), 19 deletions(-) diff --git a/sycl/source/detail/scheduler/commands.cpp b/sycl/source/detail/scheduler/commands.cpp index f061e803bb01f..44c160489ef03 100644 --- a/sycl/source/detail/scheduler/commands.cpp +++ b/sycl/source/detail/scheduler/commands.cpp @@ -714,23 +714,7 @@ bool Command::enqueue(EnqueueResultT &EnqueueResult, BlockingT Blocking, // std::string(getBlockReason()), // PI_ERROR_INVALID_OPERATION); - // #ifdef XPTI_ENABLE_INSTRUMENTATION - // // Scoped trace event notifier that emits a barrier begin and barrier - // end - // // event, which models the barrier while enqueuing along with the - // blocked - // // reason, as determined by the scheduler - // std::string Info = "enqueue.barrier["; - // Info += std::string(getBlockReason()) + "]"; - // emitInstrumentation(xpti::trace_barrier_begin, Info.c_str()); - // #endif - - // // Wait if blocking - // while (MEnqueueStatus == EnqueueResultT::SyclEnqueueBlocked) - // ; - // #ifdef XPTI_ENABLE_INSTRUMENTATION - // emitInstrumentation(xpti::trace_barrier_end, Info.c_str()); - // #endif + // } std::lock_guard Lock(MEnqueueMtx); @@ -1604,7 +1588,8 @@ void UpdateHostRequirementCommand::emitInstrumentationData() { } bool UpdateHostRequirementCommand::supportsPostEnqueueCleanup() const { - return !isBlocking(); // to think, may be moved to Command? + // TODO: consider moving to base class + return !isBlocking(); } static std::string cgTypeToString(detail::CG::CGTYPE Type) { diff --git a/sycl/source/detail/scheduler/commands.hpp b/sycl/source/detail/scheduler/commands.hpp index 3e6b802bfae05..4febb8e973384 100644 --- a/sycl/source/detail/scheduler/commands.hpp +++ b/sycl/source/detail/scheduler/commands.hpp @@ -148,6 +148,7 @@ class Command { enum class BlockReason : int { HostAccessor = 0, HostTask }; bool blockManually(const BlockReason &Reason); bool unblock(); + void extraWaitIfBlocked(); void addBlockedUserUnique(const EventImplPtr &NewUser) { if (std::find(MBlockedUsers.begin(), MBlockedUsers.end(), NewUser) != @@ -276,7 +277,7 @@ class Command { /// Contains list of commands that depend on the command. std::unordered_set MUsers; /// Indicates whether the command is set as blocking for its users. - bool MIsManuallyBlocked = false; + std::atomic_bool MIsManuallyBlocked = false; /// Counts the number of memory objects this command is a leaf for. unsigned MLeafCounter = 0; diff --git a/sycl/source/detail/scheduler/graph_processor.cpp b/sycl/source/detail/scheduler/graph_processor.cpp index b7135cf9c3484..d9df082c2bf37 100644 --- a/sycl/source/detail/scheduler/graph_processor.cpp +++ b/sycl/source/detail/scheduler/graph_processor.cpp @@ -40,8 +40,40 @@ void Scheduler::GraphProcessor::waitForEvent(const EventImplPtr &Event, assert(Cmd->getEvent() == Event); GraphReadLock.unlock(); + + static bool ThrowOnBlock = getenv("SYCL_THROW_ON_BLOCK") != nullptr; + if (ThrowOnBlock) + throw sycl::runtime_error( + std::string("Waiting for blocked command. Block reason: ") + + std::string(CmdAfterWait->getBlockReason()), + PI_ERROR_INVALID_OPERATION); Event->waitInternal(); + if (Command* CmdAfterWait = Event->getCommand() && CmdAfterWait->isBlocking()) + { + static bool ThrowOnBlock = getenv("SYCL_THROW_ON_BLOCK") != nullptr; + if (ThrowOnBlock) + throw sycl::runtime_error( + std::string("Waiting for blocked command. Block reason: ") + + std::string(CmdAfterWait->getBlockReason()), + PI_ERROR_INVALID_OPERATION); + #ifdef XPTI_ENABLE_INSTRUMENTATION + // Scoped trace event notifier that emits a barrier begin and barrier end + // event, which models the barrier while enqueuing along with the blocked + // reason, as determined by the scheduler + std::string Info = "enqueue.barrier["; + Info += std::string(Cmd->getBlockReason()) + "]"; + emitInstrumentation(xpti::trace_barrier_begin, Info.c_str()); +#endif + + // Wait if blocking. isBlocked path for task completion is handled above with Event->waitInternal(). + while (CmdAfterWait->MIsManuallyBlocked == true) + ; +#ifdef XPTI_ENABLE_INSTRUMENTATION + emitInstrumentation(xpti::trace_barrier_end, Info.c_str()); +#endif + } + if (LockTheLock) GraphReadLock.lock(); } From b634f5fd1d29989d83dd2837c36725b66ab6a56b Mon Sep 17 00:00:00 2001 From: "Tikhomirova, Kseniya" Date: Sun, 4 Dec 2022 23:31:12 -0800 Subject: [PATCH 14/22] Restore unit tests & cleanup redundant code Signed-off-by: Tikhomirova, Kseniya --- sycl/source/detail/scheduler/commands.cpp | 20 +-- .../detail/scheduler/graph_processor.cpp | 45 +----- .../scheduler/EnqueueWithDependsOnDeps.cpp | 2 +- .../scheduler/PostEnqueueCleanup.cpp | 138 +++++++++--------- 4 files changed, 75 insertions(+), 130 deletions(-) diff --git a/sycl/source/detail/scheduler/commands.cpp b/sycl/source/detail/scheduler/commands.cpp index 44c160489ef03..04df958d11df6 100644 --- a/sycl/source/detail/scheduler/commands.cpp +++ b/sycl/source/detail/scheduler/commands.cpp @@ -695,28 +695,12 @@ void Command::emitInstrumentation(uint16_t Type, const char *Txt) { bool Command::enqueue(EnqueueResultT &EnqueueResult, BlockingT Blocking, std::vector &ToCleanUp) { + assert(MEnqueueStatus != EnqueueResultT::SyclEnqueueBlocked && + "Final command enqueue should always be not blocked."); // Exit if already enqueued if (MEnqueueStatus == EnqueueResultT::SyclEnqueueSuccess) return true; - // If the command is blocked from enqueueing - // if (MIsManuallyBlockable && MEnqueueStatus == - // EnqueueResultT::SyclEnqueueBlocked) { - // // Exit if enqueue type is not blocking - // if (!Blocking) { - // EnqueueResult = EnqueueResultT(EnqueueResultT::SyclEnqueueBlocked, - // this); return false; - // } - // static bool ThrowOnBlock = getenv("SYCL_THROW_ON_BLOCK") != nullptr; - // if (ThrowOnBlock) - // throw sycl::runtime_error( - // std::string("Waiting for blocked command. Block reason: ") + - // std::string(getBlockReason()), - // PI_ERROR_INVALID_OPERATION); - - - // } - std::lock_guard Lock(MEnqueueMtx); // Exit if the command is already enqueued diff --git a/sycl/source/detail/scheduler/graph_processor.cpp b/sycl/source/detail/scheduler/graph_processor.cpp index d9df082c2bf37..ffcc31360f53b 100644 --- a/sycl/source/detail/scheduler/graph_processor.cpp +++ b/sycl/source/detail/scheduler/graph_processor.cpp @@ -40,39 +40,14 @@ void Scheduler::GraphProcessor::waitForEvent(const EventImplPtr &Event, assert(Cmd->getEvent() == Event); GraphReadLock.unlock(); - - static bool ThrowOnBlock = getenv("SYCL_THROW_ON_BLOCK") != nullptr; - if (ThrowOnBlock) - throw sycl::runtime_error( - std::string("Waiting for blocked command. Block reason: ") + - std::string(CmdAfterWait->getBlockReason()), - PI_ERROR_INVALID_OPERATION); Event->waitInternal(); - if (Command* CmdAfterWait = Event->getCommand() && CmdAfterWait->isBlocking()) - { - static bool ThrowOnBlock = getenv("SYCL_THROW_ON_BLOCK") != nullptr; - if (ThrowOnBlock) - throw sycl::runtime_error( - std::string("Waiting for blocked command. Block reason: ") + - std::string(CmdAfterWait->getBlockReason()), - PI_ERROR_INVALID_OPERATION); - #ifdef XPTI_ENABLE_INSTRUMENTATION - // Scoped trace event notifier that emits a barrier begin and barrier end - // event, which models the barrier while enqueuing along with the blocked - // reason, as determined by the scheduler - std::string Info = "enqueue.barrier["; - Info += std::string(Cmd->getBlockReason()) + "]"; - emitInstrumentation(xpti::trace_barrier_begin, Info.c_str()); -#endif - - // Wait if blocking. isBlocked path for task completion is handled above with Event->waitInternal(). + if (Command *CmdAfterWait = static_cast(Event->getCommand()); + CmdAfterWait && CmdAfterWait->isBlocking()) + // Wait if blocking. isBlocked path for task completion is handled above + // with Event->waitInternal(). while (CmdAfterWait->MIsManuallyBlocked == true) ; -#ifdef XPTI_ENABLE_INSTRUMENTATION - emitInstrumentation(xpti::trace_barrier_end, Info.c_str()); -#endif - } if (LockTheLock) GraphReadLock.lock(); @@ -82,22 +57,14 @@ bool Scheduler::GraphProcessor::handleBlockingCmd(Command *Cmd, EnqueueResultT &EnqueueResult, Command *RootCommand, BlockingT Blocking) { - - static bool ThrowOnBlock = getenv("SYCL_THROW_ON_BLOCK") != nullptr; + // No error to be returned for root command. - // Blocking && !ThrowOnBlock means that we will wait for task in parent command enqueue if it is blocking and do not report it to user. - if ((Cmd == RootCommand) || (Blocking && !ThrowOnBlock)) + if (Cmd == RootCommand || Blocking) return true; { std::lock_guard Guard(Cmd->MBlockedUsersMutex); if (Cmd->isBlocking()) { - if (Blocking && ThrowOnBlock) - // Means that we are going to wait on Blocking command - throw sycl::runtime_error( - std::string("Waiting for blocked command. Block reason: ") + - std::string(Cmd->getBlockReason()), - PI_ERROR_INVALID_OPERATION); const EventImplPtr &RootCmdEvent = RootCommand->getEvent(); Cmd->addBlockedUserUnique(RootCmdEvent); EnqueueResult = EnqueueResultT(EnqueueResultT::SyclEnqueueBlocked, Cmd); diff --git a/sycl/unittests/scheduler/EnqueueWithDependsOnDeps.cpp b/sycl/unittests/scheduler/EnqueueWithDependsOnDeps.cpp index f111bebd4e663..2632c0f96dda2 100644 --- a/sycl/unittests/scheduler/EnqueueWithDependsOnDeps.cpp +++ b/sycl/unittests/scheduler/EnqueueWithDependsOnDeps.cpp @@ -269,7 +269,7 @@ TEST_F(DependsOnTests, EnqueueNoMemObjDoubleKernelDepHost) { detail::Command *Cmd1 = AddTaskCG(TestCGType::HOST_TASK, Events); EventImplPtr Cmd1Event = Cmd1->getEvent(); - Cmd1->MEnqueueStatus = detail::EnqueueResultT::SyclEnqueueBlocked; + Cmd1->blockManually(detail::Command::BlockReason::HostAccessor); // Depends on host task Events.push_back(Cmd1Event); diff --git a/sycl/unittests/scheduler/PostEnqueueCleanup.cpp b/sycl/unittests/scheduler/PostEnqueueCleanup.cpp index d3581e1f3a55c..19c3ff2772b63 100644 --- a/sycl/unittests/scheduler/PostEnqueueCleanup.cpp +++ b/sycl/unittests/scheduler/PostEnqueueCleanup.cpp @@ -198,79 +198,73 @@ static void checkCleanupOnLeafUpdate( MS.removeRecordForMemObj(detail::getSyclObjImpl(Buf).get()); } -// TEST_F(SchedulerTest, PostEnqueueCleanup) { -// // Enforce creation of linked commands to test all sites of calling cleanup. -// unittest::ScopedEnvVar HostUnifiedMemoryVar{ -// HostUnifiedMemoryName, "1", -// detail::SYCLConfig::reset}; -// sycl::unittest::PiMock Mock; -// sycl::platform Plt = Mock.getPlatform(); -// Mock.redefineBefore( -// redefinedEnqueueMemBufferMap); -// Mock.redefineBefore( -// redefinedEnqueueMemUnmap); -// Mock.redefineBefore( -// redefinedEnqueueMemBufferFill); +TEST_F(SchedulerTest, PostEnqueueCleanup) { + // Enforce creation of linked commands to test all sites of calling cleanup. + unittest::ScopedEnvVar HostUnifiedMemoryVar{ + HostUnifiedMemoryName, "1", + detail::SYCLConfig::reset}; + sycl::unittest::PiMock Mock; + sycl::platform Plt = Mock.getPlatform(); + Mock.redefineBefore( + redefinedEnqueueMemBufferMap); + Mock.redefineBefore( + redefinedEnqueueMemUnmap); + Mock.redefineBefore( + redefinedEnqueueMemBufferFill); -// context Ctx{Plt}; -// queue Queue{Ctx, default_selector_v}; -// detail::QueueImplPtr QueueImpl = detail::getSyclObjImpl(Queue); -// MockScheduler MS; + context Ctx{Plt}; + queue Queue{Ctx, default_selector_v}; + detail::QueueImplPtr QueueImpl = detail::getSyclObjImpl(Queue); + MockScheduler MS; -// buffer Buf{range<1>(1)}; -// std::shared_ptr BufImpl = detail::getSyclObjImpl(Buf); -// detail::Requirement MockReq = getMockRequirement(Buf); -// MockReq.MDims = 1; -// MockReq.MSYCLMemObj = BufImpl.get(); + buffer Buf{range<1>(1)}; + std::shared_ptr BufImpl = detail::getSyclObjImpl(Buf); + detail::Requirement MockReq = getMockRequirement(Buf); + MockReq.MDims = 1; + MockReq.MSYCLMemObj = BufImpl.get(); -// checkCleanupOnEnqueue(MS, QueueImpl, Buf, MockReq); -// std::vector ToEnqueue; -// checkCleanupOnLeafUpdate(MS, QueueImpl, Buf, MockReq, -// [&](detail::MemObjRecord *Record) { -// MS.decrementLeafCountersForRecord(Record); -// }); -// checkCleanupOnLeafUpdate( -// MS, QueueImpl, Buf, MockReq, [&](detail::MemObjRecord *Record) { -// MS.insertMemoryMove(Record, &MockReq, QueueImpl, ToEnqueue); -// }); -// checkCleanupOnLeafUpdate(MS, QueueImpl, Buf, MockReq, -// [&](detail::MemObjRecord *Record) { -// Record->MMemModified = true; -// MS.addCopyBack(&MockReq, ToEnqueue); -// }); -// checkCleanupOnLeafUpdate( -// MS, QueueImpl, Buf, MockReq, [&](detail::MemObjRecord *Record) { -// detail::Command *Leaf = *Record->MWriteLeaves.begin(); -// MS.addEmptyCmd(Leaf, {&MockReq}, QueueImpl, -// detail::Command::BlockReason::HostTask, ToEnqueue); -// }); -// device HostDevice = detail::createSyclObjFromImpl( -// detail::device_impl::getHostDeviceImpl()); -// detail::QueueImplPtr DefaultHostQueue{ -// new detail::queue_impl(detail::getSyclObjImpl(HostDevice), {}, {})}; -// checkCleanupOnLeafUpdate( -// MS, DefaultHostQueue, Buf, MockReq, [&](detail::MemObjRecord *Record) { -// MS.getOrCreateAllocaForReq(Record, &MockReq, QueueImpl, ToEnqueue); -// }); -// // Check cleanup on exceeding leaf limit. -// checkCleanupOnLeafUpdate( -// MS, QueueImpl, Buf, MockReq, [&](detail::MemObjRecord *Record) { -// std::vector> Leaves; -// for (std::size_t I = 0; -// I < Record->MWriteLeaves.genericCommandsCapacity(); ++I) -// Leaves.push_back(std::make_unique(QueueImpl, MockReq)); + checkCleanupOnEnqueue(MS, QueueImpl, Buf, MockReq); + std::vector ToEnqueue; + checkCleanupOnLeafUpdate(MS, QueueImpl, Buf, MockReq, + [&](detail::MemObjRecord *Record) { + MS.decrementLeafCountersForRecord(Record); + }); + checkCleanupOnLeafUpdate( + MS, QueueImpl, Buf, MockReq, [&](detail::MemObjRecord *Record) { + MS.insertMemoryMove(Record, &MockReq, QueueImpl, ToEnqueue); + }); + checkCleanupOnLeafUpdate(MS, QueueImpl, Buf, MockReq, + [&](detail::MemObjRecord *Record) { + Record->MMemModified = true; + MS.addCopyBack(&MockReq, ToEnqueue); + }); + device HostDevice = detail::createSyclObjFromImpl( + detail::device_impl::getHostDeviceImpl()); + detail::QueueImplPtr DefaultHostQueue{ + new detail::queue_impl(detail::getSyclObjImpl(HostDevice), {}, {})}; + checkCleanupOnLeafUpdate( + MS, DefaultHostQueue, Buf, MockReq, [&](detail::MemObjRecord *Record) { + MS.getOrCreateAllocaForReq(Record, &MockReq, QueueImpl, ToEnqueue); + }); + // Check cleanup on exceeding leaf limit. + checkCleanupOnLeafUpdate( + MS, QueueImpl, Buf, MockReq, [&](detail::MemObjRecord *Record) { + std::vector> Leaves; + for (std::size_t I = 0; + I < Record->MWriteLeaves.genericCommandsCapacity(); ++I) + Leaves.push_back(std::make_unique(QueueImpl, MockReq)); -// detail::AllocaCommandBase *AllocaCmd = Record->MAllocaCommands[0]; -// std::vector ToCleanUp; -// for (std::unique_ptr &MockCmd : Leaves) { -// (void)MockCmd->addDep(detail::DepDesc(AllocaCmd, &MockReq, AllocaCmd), -// ToCleanUp); -// MS.addNodeToLeaves(Record, MockCmd.get(), access::mode::read_write, -// ToEnqueue); -// } -// for (std::unique_ptr &MockCmd : Leaves) -// MS.updateLeaves({MockCmd.get()}, Record, access::mode::read_write, -// ToCleanUp); -// EXPECT_TRUE(ToCleanUp.empty()); -// }); -// } + detail::AllocaCommandBase *AllocaCmd = Record->MAllocaCommands[0]; + std::vector ToCleanUp; + for (std::unique_ptr &MockCmd : Leaves) { + (void)MockCmd->addDep(detail::DepDesc(AllocaCmd, &MockReq, AllocaCmd), + ToCleanUp); + MS.addNodeToLeaves(Record, MockCmd.get(), access::mode::read_write, + ToEnqueue); + } + for (std::unique_ptr &MockCmd : Leaves) + MS.updateLeaves({MockCmd.get()}, Record, access::mode::read_write, + ToCleanUp); + EXPECT_TRUE(ToCleanUp.empty()); + }); +} From 3c9bb1a59d59ac3f4989f6329db0693c074acffa Mon Sep 17 00:00:00 2001 From: "Tikhomirova, Kseniya" Date: Thu, 8 Dec 2022 07:54:24 -0800 Subject: [PATCH 15/22] Fix test Signed-off-by: Tikhomirova, Kseniya --- .../scheduler/EnqueueWithDependsOnDeps.cpp | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/sycl/unittests/scheduler/EnqueueWithDependsOnDeps.cpp b/sycl/unittests/scheduler/EnqueueWithDependsOnDeps.cpp index 2632c0f96dda2..478512200a3d7 100644 --- a/sycl/unittests/scheduler/EnqueueWithDependsOnDeps.cpp +++ b/sycl/unittests/scheduler/EnqueueWithDependsOnDeps.cpp @@ -283,5 +283,29 @@ TEST_F(DependsOnTests, EnqueueNoMemObjDoubleKernelDepHost) { EventImplPtr Cmd3Event = Cmd3->getEvent(); std::vector BlockedCommands{Cmd2, Cmd3}; - VerifyBlockedCommandsEnqueue(Cmd1, BlockedCommands); + detail::EnqueueResultT Result; + for (detail::Command *BlockedCmd : BlockedCommands) { + EXPECT_FALSE(MS.enqueueCommand(BlockedCmd, Result, + detail::BlockingT::NON_BLOCKING)); + EXPECT_EQ(Result.MResult, detail::EnqueueResultT::SyclEnqueueBlocked); + EXPECT_EQ(Result.MCmd, static_cast(Cmd1)); + EXPECT_FALSE(BlockedCmd->isSuccessfullyEnqueued()); + } + EXPECT_TRUE(Cmd1->isSuccessfullyEnqueued()); + + Cmd1->unblock(); + + auto BlockingEvent = Cmd1->getEvent(); + BlockingEvent->wait(BlockingEvent); + { + auto Lock = MS.acquireOriginSchedGraphWriteLock(); + Lock.lock(); + for (detail::Command *BlockedCmd : BlockedCommands) { + EXPECT_TRUE(BlockedCmd->isSuccessfullyEnqueued()); + } + } + for (detail::Command *BlockedCmd : BlockedCommands) { + auto BlockedEvent = BlockedCmd->getEvent(); + BlockedEvent->wait(BlockedEvent); + } } \ No newline at end of file From 247db969485a3a21b56956958aec4e772f0bfef8 Mon Sep 17 00:00:00 2001 From: "Tikhomirova, Kseniya" Date: Sun, 11 Dec 2022 11:39:24 -0800 Subject: [PATCH 16/22] Fix waiting of manually blocked command Signed-off-by: Tikhomirova, Kseniya --- sycl/source/detail/scheduler/commands.hpp | 2 +- .../detail/scheduler/graph_processor.cpp | 23 ++++++----- sycl/source/detail/scheduler/scheduler.cpp | 8 +++- sycl/test/scheduler/HostAcc.cpp | 38 ------------------- 4 files changed, 18 insertions(+), 53 deletions(-) delete mode 100644 sycl/test/scheduler/HostAcc.cpp diff --git a/sycl/source/detail/scheduler/commands.hpp b/sycl/source/detail/scheduler/commands.hpp index 4febb8e973384..a54a4914279b1 100644 --- a/sycl/source/detail/scheduler/commands.hpp +++ b/sycl/source/detail/scheduler/commands.hpp @@ -341,7 +341,7 @@ class Command { /// Using EventImplPtr since enqueueUnblockedCommands and event.wait may /// intersect with command enqueue. std::vector MBlockedUsers; - std::mutex MBlockedUsersMutex; + std::recursive_mutex MBlockedUsersMutex; }; /// The release command enqueues release of a memory object instance allocated diff --git a/sycl/source/detail/scheduler/graph_processor.cpp b/sycl/source/detail/scheduler/graph_processor.cpp index ffcc31360f53b..647166c61f249 100644 --- a/sycl/source/detail/scheduler/graph_processor.cpp +++ b/sycl/source/detail/scheduler/graph_processor.cpp @@ -42,13 +42,6 @@ void Scheduler::GraphProcessor::waitForEvent(const EventImplPtr &Event, GraphReadLock.unlock(); Event->waitInternal(); - if (Command *CmdAfterWait = static_cast(Event->getCommand()); - CmdAfterWait && CmdAfterWait->isBlocking()) - // Wait if blocking. isBlocked path for task completion is handled above - // with Event->waitInternal(). - while (CmdAfterWait->MIsManuallyBlocked == true) - ; - if (LockTheLock) GraphReadLock.lock(); } @@ -59,12 +52,18 @@ bool Scheduler::GraphProcessor::handleBlockingCmd(Command *Cmd, BlockingT Blocking) { // No error to be returned for root command. - if (Cmd == RootCommand || Blocking) + if (Cmd == RootCommand) return true; - - { - std::lock_guard Guard(Cmd->MBlockedUsersMutex); - if (Cmd->isBlocking()) { + if (Blocking) { + // If Blocking & isBlocked -> we will successfully wait for event in + // enqueueImp. MIsManuallyBlocked should block enqueueCommand with BLOCKING + // and could not be moved to event.wait because should not affect simple + // wait case when no users of blocking command. + while (Cmd->MIsManuallyBlocked) + ; + } else { + std::lock_guard Guard(Cmd->MBlockedUsersMutex); + if (Cmd->isBlocking() || Cmd->MIsManuallyBlocked) { const EventImplPtr &RootCmdEvent = RootCommand->getEvent(); Cmd->addBlockedUserUnique(RootCmdEvent); EnqueueResult = EnqueueResultT(EnqueueResultT::SyclEnqueueBlocked, Cmd); diff --git a/sycl/source/detail/scheduler/scheduler.cpp b/sycl/source/detail/scheduler/scheduler.cpp index 526377422cee6..43652894af920 100644 --- a/sycl/source/detail/scheduler/scheduler.cpp +++ b/sycl/source/detail/scheduler/scheduler.cpp @@ -344,7 +344,11 @@ void Scheduler::releaseHostAccessor(Requirement *Req) { assert(BlockedCmd && "Can't find appropriate command to unblock"); - BlockedCmd->unblock(); + { + std::lock_guard Guard( + BlockedCmd->MBlockedUsersMutex); + BlockedCmd->unblock(); + } enqueueLeavesOfReqUnlocked(Req, ToCleanUp); } @@ -478,7 +482,7 @@ void Scheduler::NotifyHostTaskCompletion(Command *Cmd) { std::vector Deps = Cmd->MDeps; { - std::lock_guard Guard(Cmd->MBlockedUsersMutex); + std::lock_guard Guard(Cmd->MBlockedUsersMutex); // update self-event status Cmd->getEvent()->setComplete(); } diff --git a/sycl/test/scheduler/HostAcc.cpp b/sycl/test/scheduler/HostAcc.cpp deleted file mode 100644 index 285c293166083..0000000000000 --- a/sycl/test/scheduler/HostAcc.cpp +++ /dev/null @@ -1,38 +0,0 @@ -// RUN: %clangxx -fsycl -I %sycl_source_dir %s -o %t.out -// RUN: env SYCL_THROW_ON_BLOCK=1 %t.out -//==--------------------------- HostAcc.cpp --------------------------------==// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#include - -int main() { - - bool Fail = false; - // Check that multiple host accessors which are created with read access mode - // can exist simultaneously. - sycl::buffer Buf(sycl::range<1>{1}); - { - auto Acc1 = Buf.get_access(); - auto Acc2 = Buf.get_access(); - } - - // Check that exception is thrown in a dead lock scenario if special - // environment variable is set. - { - bool ExcCaught = false; - try { - auto Acc1 = Buf.get_access(); - auto Acc3 = Buf.get_access(); - } catch (sycl::runtime_error &E) { - ExcCaught = true; - } - Fail |= !ExcCaught; - } - - return Fail; -} From 649bb80bb3553aa0df4c31f61d351e5f175d8dae Mon Sep 17 00:00:00 2001 From: "Tikhomirova, Kseniya" Date: Mon, 12 Dec 2022 07:35:41 -0800 Subject: [PATCH 17/22] Recursive mutex is not needed after fix Signed-off-by: Tikhomirova, Kseniya --- sycl/source/detail/scheduler/commands.hpp | 2 +- sycl/source/detail/scheduler/graph_processor.cpp | 2 +- sycl/source/detail/scheduler/scheduler.cpp | 5 ++--- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/sycl/source/detail/scheduler/commands.hpp b/sycl/source/detail/scheduler/commands.hpp index a54a4914279b1..4febb8e973384 100644 --- a/sycl/source/detail/scheduler/commands.hpp +++ b/sycl/source/detail/scheduler/commands.hpp @@ -341,7 +341,7 @@ class Command { /// Using EventImplPtr since enqueueUnblockedCommands and event.wait may /// intersect with command enqueue. std::vector MBlockedUsers; - std::recursive_mutex MBlockedUsersMutex; + std::mutex MBlockedUsersMutex; }; /// The release command enqueues release of a memory object instance allocated diff --git a/sycl/source/detail/scheduler/graph_processor.cpp b/sycl/source/detail/scheduler/graph_processor.cpp index 647166c61f249..d7cb4bc9236cb 100644 --- a/sycl/source/detail/scheduler/graph_processor.cpp +++ b/sycl/source/detail/scheduler/graph_processor.cpp @@ -62,7 +62,7 @@ bool Scheduler::GraphProcessor::handleBlockingCmd(Command *Cmd, while (Cmd->MIsManuallyBlocked) ; } else { - std::lock_guard Guard(Cmd->MBlockedUsersMutex); + std::lock_guard Guard(Cmd->MBlockedUsersMutex); if (Cmd->isBlocking() || Cmd->MIsManuallyBlocked) { const EventImplPtr &RootCmdEvent = RootCommand->getEvent(); Cmd->addBlockedUserUnique(RootCmdEvent); diff --git a/sycl/source/detail/scheduler/scheduler.cpp b/sycl/source/detail/scheduler/scheduler.cpp index 43652894af920..ebdb052d0f6e7 100644 --- a/sycl/source/detail/scheduler/scheduler.cpp +++ b/sycl/source/detail/scheduler/scheduler.cpp @@ -345,8 +345,7 @@ void Scheduler::releaseHostAccessor(Requirement *Req) { assert(BlockedCmd && "Can't find appropriate command to unblock"); { - std::lock_guard Guard( - BlockedCmd->MBlockedUsersMutex); + std::lock_guard Guard(BlockedCmd->MBlockedUsersMutex); BlockedCmd->unblock(); } @@ -482,7 +481,7 @@ void Scheduler::NotifyHostTaskCompletion(Command *Cmd) { std::vector Deps = Cmd->MDeps; { - std::lock_guard Guard(Cmd->MBlockedUsersMutex); + std::lock_guard Guard(Cmd->MBlockedUsersMutex); // update self-event status Cmd->getEvent()->setComplete(); } From c9eaa53fb2514af7d61859c1a2bfa942509720ec Mon Sep 17 00:00:00 2001 From: "Tikhomirova, Kseniya" Date: Mon, 12 Dec 2022 12:26:32 -0800 Subject: [PATCH 18/22] Add tests Signed-off-by: Tikhomirova, Kseniya --- .../scheduler/EnqueueWithDependsOnDeps.cpp | 55 ++++++++++++++++++- 1 file changed, 52 insertions(+), 3 deletions(-) diff --git a/sycl/unittests/scheduler/EnqueueWithDependsOnDeps.cpp b/sycl/unittests/scheduler/EnqueueWithDependsOnDeps.cpp index 478512200a3d7..2654f2edaf020 100644 --- a/sycl/unittests/scheduler/EnqueueWithDependsOnDeps.cpp +++ b/sycl/unittests/scheduler/EnqueueWithDependsOnDeps.cpp @@ -13,6 +13,8 @@ #include #include +#include + #include using namespace sycl; @@ -285,8 +287,8 @@ TEST_F(DependsOnTests, EnqueueNoMemObjDoubleKernelDepHost) { std::vector BlockedCommands{Cmd2, Cmd3}; detail::EnqueueResultT Result; for (detail::Command *BlockedCmd : BlockedCommands) { - EXPECT_FALSE(MS.enqueueCommand(BlockedCmd, Result, - detail::BlockingT::NON_BLOCKING)); + EXPECT_FALSE( + MS.enqueueCommand(BlockedCmd, Result, detail::BlockingT::NON_BLOCKING)); EXPECT_EQ(Result.MResult, detail::EnqueueResultT::SyclEnqueueBlocked); EXPECT_EQ(Result.MCmd, static_cast(Cmd1)); EXPECT_FALSE(BlockedCmd->isSuccessfullyEnqueued()); @@ -308,4 +310,51 @@ TEST_F(DependsOnTests, EnqueueNoMemObjDoubleKernelDepHost) { auto BlockedEvent = BlockedCmd->getEvent(); BlockedEvent->wait(BlockedEvent); } -} \ No newline at end of file +} + +TEST_F(DependsOnTests, TwoHostAccessorsReadRead) { + buffer Buf{range<1>(1)}; + std::shared_ptr BufImpl = detail::getSyclObjImpl(Buf); + detail::Requirement MockReq = getMockRequirement(Buf); + MockReq.MDims = 1; + MockReq.MSYCLMemObj = BufImpl.get(); + MockReq.MAccessMode = access::mode::read; + auto EventImplRead = MS.addHostAccessor(&MockReq); + auto EventImplReadSecond = MS.addHostAccessor(&MockReq); + detail::Command *CmdRead = + static_cast(EventImplRead->getCommand()); + detail::Command *CmdReadSecond = + static_cast(EventImplReadSecond->getCommand()); + EXPECT_EQ(CmdRead->MUsers.size(), 0u); + bool DepExists = any_of( + CmdReadSecond->MDeps.begin(), CmdReadSecond->MDeps.end(), + [&CmdRead](detail::DepDesc &Dep) { return CmdRead == Dep.MDepCommand; }); + EXPECT_FALSE(DepExists); + MS.releaseHostAccessor(&MockReq); +} + +TEST_F(DependsOnTests, TwoHostAccessorsReadWrite) { + buffer Buf{range<1>(1)}; + std::shared_ptr BufImpl = detail::getSyclObjImpl(Buf); + detail::Requirement MockReq = getMockRequirement(Buf); + MockReq.MDims = 1; + MockReq.MSYCLMemObj = BufImpl.get(); + MockReq.MAccessMode = access::mode::read; + auto EventImplRead = MS.addHostAccessor(&MockReq); + detail::Requirement MockReq2 = getMockRequirement(Buf); + MockReq2.MDims = 1; + MockReq2.MSYCLMemObj = BufImpl.get(); + MockReq2.MAccessMode = access::mode::discard_write; + auto EventImplWrite = MS.addHostAccessor(&MockReq2); + detail::Command *CmdRead = + static_cast(EventImplRead->getCommand()); + detail::Command *CmdWrite = + static_cast(EventImplWrite->getCommand()); + EXPECT_EQ(CmdRead->MUsers.size(), 1u); + bool DepExists = any_of( + CmdWrite->MDeps.begin(), CmdWrite->MDeps.end(), + [&CmdRead](detail::DepDesc &Dep) { return CmdRead == Dep.MDepCommand; }); + EXPECT_TRUE(DepExists); + MS.releaseHostAccessor(&MockReq); + MS.releaseHostAccessor(&MockReq2); +} From d23c5ec2a07f3dc0d01b520e61f5d8c703d01d92 Mon Sep 17 00:00:00 2001 From: "Tikhomirova, Kseniya" Date: Mon, 19 Dec 2022 07:07:44 -0800 Subject: [PATCH 19/22] remove unused function Signed-off-by: Tikhomirova, Kseniya --- sycl/source/detail/scheduler/commands.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/sycl/source/detail/scheduler/commands.hpp b/sycl/source/detail/scheduler/commands.hpp index 4febb8e973384..e6c172372d2e7 100644 --- a/sycl/source/detail/scheduler/commands.hpp +++ b/sycl/source/detail/scheduler/commands.hpp @@ -148,7 +148,6 @@ class Command { enum class BlockReason : int { HostAccessor = 0, HostTask }; bool blockManually(const BlockReason &Reason); bool unblock(); - void extraWaitIfBlocked(); void addBlockedUserUnique(const EventImplPtr &NewUser) { if (std::find(MBlockedUsers.begin(), MBlockedUsers.end(), NewUser) != From 76388ae04fc1f739a45f88c4e361ea264ea7a4ed Mon Sep 17 00:00:00 2001 From: "Tikhomirova, Kseniya" Date: Mon, 19 Dec 2022 08:10:39 -0800 Subject: [PATCH 20/22] more tests Signed-off-by: Tikhomirova, Kseniya --- sycl/source/detail/scheduler/commands.hpp | 4 +- sycl/source/detail/scheduler/scheduler.cpp | 2 +- .../scheduler/EnqueueWithDependsOnDeps.cpp | 41 +++++++++++++++++++ 3 files changed, 44 insertions(+), 3 deletions(-) diff --git a/sycl/source/detail/scheduler/commands.hpp b/sycl/source/detail/scheduler/commands.hpp index 7f52b3e9b4940..2b322aad78f70 100644 --- a/sycl/source/detail/scheduler/commands.hpp +++ b/sycl/source/detail/scheduler/commands.hpp @@ -289,8 +289,8 @@ class Command { /// Used for marking the node during graph traversal. Marks MMarks; - // Only have reasonable value while MIsManuallyBlocked is true - BlockReason MBlockReason; + // Only have reasonable value while isBlocking returns true + BlockReason MBlockReason = BlockReason::HostTask; /// Describes the status of the command. std::atomic MEnqueueStatus; diff --git a/sycl/source/detail/scheduler/scheduler.cpp b/sycl/source/detail/scheduler/scheduler.cpp index 3295563587353..1712f88729e80 100644 --- a/sycl/source/detail/scheduler/scheduler.cpp +++ b/sycl/source/detail/scheduler/scheduler.cpp @@ -364,7 +364,7 @@ void Scheduler::releaseHostAccessor(Requirement *Req) { std::lock_guard Guard(BlockedCmd->MBlockedUsersMutex); BlockedCmd->unblock(); } - + // TODO: consider replacement with enqueueUnblockedCommands enqueueLeavesOfReqUnlocked(Req, ToCleanUp); } cleanupCommands(ToCleanUp); diff --git a/sycl/unittests/scheduler/EnqueueWithDependsOnDeps.cpp b/sycl/unittests/scheduler/EnqueueWithDependsOnDeps.cpp index 16fe6cfdbd35f..56c6752f50e4b 100644 --- a/sycl/unittests/scheduler/EnqueueWithDependsOnDeps.cpp +++ b/sycl/unittests/scheduler/EnqueueWithDependsOnDeps.cpp @@ -130,6 +130,9 @@ class DependsOnTests : public ::testing::Test { detail::BlockingT::NON_BLOCKING)); EXPECT_EQ(Result.MResult, detail::EnqueueResultT::SyclEnqueueBlocked); EXPECT_EQ(Result.MCmd, static_cast(BlockingCommand)); + EXPECT_EQ(Result.MCmd->MBlockReason, + detail::Command::BlockReason::HostTask); + EXPECT_FALSE(BlockedCmd->isSuccessfullyEnqueued()); } EXPECT_TRUE(BlockingCommand->isSuccessfullyEnqueued()); @@ -253,6 +256,8 @@ TEST_F(DependsOnTests, EnqueueNoMemObjDoubleKernelDepHostBlocked) { MS.enqueueCommand(Cmd3, Result, detail::BlockingT::NON_BLOCKING)); EXPECT_EQ(Result.MResult, detail::EnqueueResultT::SyclEnqueueBlocked); EXPECT_EQ(Result.MCmd, static_cast(Cmd1)); + EXPECT_EQ(Result.MCmd->MBlockReason, + detail::Command::BlockReason::HostAccessor); // Preconditions for post enqueue checks EXPECT_TRUE(Cmd1->isSuccessfullyEnqueued()); @@ -310,3 +315,39 @@ TEST_F(DependsOnTests, TwoHostAccessorsReadWrite) { MS.releaseHostAccessor(&MockReq); MS.releaseHostAccessor(&MockReq2); } + +TEST_F(DependsOnTests, EnqueueKernelDepHostAcc) { + buffer Buf{range<1>(1)}; + std::shared_ptr BufImpl = detail::getSyclObjImpl(Buf); + detail::Requirement MockReq = getMockRequirement(Buf); + MockReq.MDims = 1; + MockReq.MSYCLMemObj = BufImpl.get(); + MockReq.MAccessMode = access::mode::write; + auto EventImplRead = MS.addHostAccessor(&MockReq); + + event KEvent = QueueDevImpl->submit( + [&](handler &Cgh) { + auto acc = Buf.template get_access(Cgh); + constexpr size_t KS = sizeof(decltype(acc)); + Cgh.single_task>([=]() { (void)acc; }); + }, + QueueDevImpl, nullptr, {}); + std::shared_ptr KernelEventImpl = + detail::getSyclObjImpl(KEvent); + detail::Command *KernelCmd = + static_cast(KernelEventImpl->getCommand()); + ASSERT_NE(KernelCmd, nullptr); + EXPECT_FALSE(KernelCmd->isSuccessfullyEnqueued()); + + detail::EnqueueResultT Result; + EXPECT_FALSE( + MS.enqueueCommand(KernelCmd, Result, detail::BlockingT::NON_BLOCKING)); + EXPECT_EQ(Result.MResult, detail::EnqueueResultT::SyclEnqueueBlocked); + EXPECT_EQ(Result.MCmd, + static_cast(EventImplRead->getCommand())); + EXPECT_EQ(Result.MCmd->MBlockReason, + detail::Command::BlockReason::HostAccessor); + + MS.releaseHostAccessor(&MockReq); + EXPECT_TRUE(KernelCmd->isSuccessfullyEnqueued()); +} From adf898a85cbfc1695f80729f9bfff3432bcd8a46 Mon Sep 17 00:00:00 2001 From: "Tikhomirova, Kseniya" Date: Mon, 19 Dec 2022 08:15:32 -0800 Subject: [PATCH 21/22] fix order Signed-off-by: Tikhomirova, Kseniya --- sycl/source/detail/scheduler/commands.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sycl/source/detail/scheduler/commands.cpp b/sycl/source/detail/scheduler/commands.cpp index 33c681c9ae619..98fd88c36cddc 100644 --- a/sycl/source/detail/scheduler/commands.cpp +++ b/sycl/source/detail/scheduler/commands.cpp @@ -231,8 +231,8 @@ bool Command::isHostTask() const { bool Command::blockManually(const BlockReason &Reason) { if (MIsManuallyBlocked) return false; - MIsManuallyBlocked = true; MBlockReason = Reason; + MIsManuallyBlocked = true; return true; } From a51b53a827f4f764af469cc704fbee0abb160a71 Mon Sep 17 00:00:00 2001 From: "Tikhomirova, Kseniya" Date: Tue, 20 Dec 2022 05:18:04 -0800 Subject: [PATCH 22/22] Fix merge issues Signed-off-by: Tikhomirova, Kseniya --- sycl/source/detail/scheduler/commands.cpp | 8 +------- sycl/source/detail/scheduler/commands.hpp | 2 -- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/sycl/source/detail/scheduler/commands.cpp b/sycl/source/detail/scheduler/commands.cpp index b8461df2f8aa0..080c5d52add15 100644 --- a/sycl/source/detail/scheduler/commands.cpp +++ b/sycl/source/detail/scheduler/commands.cpp @@ -636,7 +636,7 @@ bool Command::supportsPostEnqueueCleanup() const { return true; } bool Command::readyForCleanup() const { return MLeafCounter == 0 && - MEnqueueStatus == EnqueueResultT::SyclEnqueueSuccess; + MEnqueueStatus == EnqueueResultT::SyclEnqueueSuccess && !isBlocking(); } Command *Command::addDep(DepDesc NewDep, std::vector &ToCleanUp) { @@ -2501,12 +2501,6 @@ bool ExecCGCommand::supportsPostEnqueueCleanup() const { (MCommandGroup->getType() != CG::CGTYPE::CodeplayHostTask); } -bool ExecCGCommand::readyForCleanup() const { - if (MCommandGroup->getType() == CG::CGTYPE::CodeplayHostTask) - return MLeafCounter == 0 && MEvent->isCompleted(); - return Command::readyForCleanup(); -} - KernelFusionCommand::KernelFusionCommand(QueueImplPtr Queue) : Command(Command::CommandType::FUSION, Queue), MStatus(FusionStatus::ACTIVE) { diff --git a/sycl/source/detail/scheduler/commands.hpp b/sycl/source/detail/scheduler/commands.hpp index c26ac3cee29a7..6273accdad92e 100644 --- a/sycl/source/detail/scheduler/commands.hpp +++ b/sycl/source/detail/scheduler/commands.hpp @@ -570,8 +570,6 @@ class ExecCGCommand : public Command { bool supportsPostEnqueueCleanup() const final; - bool readyForCleanup() const final; - private: pi_int32 enqueueImp() final;