Skip to content

Commit a6ccf6a

Browse files
committed
[SYCL][Fusion] Unit-test kernel fusion scheduler
Signed-off-by: Lukas Sommer <[email protected]>
1 parent 30d9ae7 commit a6ccf6a

File tree

8 files changed

+165
-10
lines changed

8 files changed

+165
-10
lines changed

sycl/source/detail/helpers.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ std::vector<RT::PiEvent> getOrWaitEvents(std::vector<sycl::event> DepEvents,
3232
continue;
3333
}
3434
// The fusion command and its event are associated with a non-host context,
35-
// but still does not produce a PI event,.
35+
// but still does not produce a PI event.
3636
bool NoPiEvent =
3737
SyclEventImplPtr->MCommand &&
3838
!static_cast<Command *>(SyclEventImplPtr->MCommand)->producesPiEvent();

sycl/source/detail/scheduler/graph_builder.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1367,7 +1367,7 @@ void Scheduler::GraphBuilder::removeNodeFromGraph(
13671367
auto *DepReq = PrevDep.MDepRequirement;
13681368
auto *DepRecord = getMemObjRecord(DepReq->MSYCLMemObj);
13691369
if (DepRecord == Record) {
1370-
// Need to restore this as a leave, because we pushed it from the
1370+
// Need to restore this as a leaf, because we pushed it from the
13711371
// leaves when adding the placeholder command.
13721372
assert(Dep.MDepCommand);
13731373
addNodeToLeaves(Record, Dep.MDepCommand, DepReq->MAccessMode,

sycl/source/detail/scheduler/scheduler.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -565,14 +565,14 @@ void Scheduler::cleanupAuxiliaryResources(BlockingT Blocking) {
565565
thread_local bool Scheduler::ForceDeferredMemObjRelease = false;
566566

567567
void Scheduler::startFusion(QueueImplPtr Queue) {
568-
WriteLockT Lock(MGraphLock, std::defer_lock);
568+
WriteLockT Lock = acquireWriteLock();
569569
MGraphBuilder.startFusion(Queue);
570570
}
571571

572572
void Scheduler::cancelFusion(QueueImplPtr Queue) {
573573
std::vector<Command *> ToEnqueue;
574574
{
575-
WriteLockT Lock{MGraphLock, std::defer_lock};
575+
WriteLockT Lock = acquireWriteLock();
576576
MGraphBuilder.cancelFusion(Queue, ToEnqueue);
577577
}
578578
enqueueCommandForCG(nullptr, ToEnqueue);
@@ -583,7 +583,7 @@ EventImplPtr Scheduler::completeFusion(QueueImplPtr Queue,
583583
std::vector<Command *> ToEnqueue;
584584
EventImplPtr FusedEvent;
585585
{
586-
WriteLockT Lock{MGraphLock, std::defer_lock};
586+
WriteLockT Lock = acquireWriteLock();
587587
FusedEvent = MGraphBuilder.completeFusion(Queue, ToEnqueue, PropList);
588588
}
589589
enqueueCommandForCG(nullptr, ToEnqueue);
@@ -592,7 +592,7 @@ EventImplPtr Scheduler::completeFusion(QueueImplPtr Queue,
592592
}
593593

594594
bool Scheduler::isInFusionMode(QueueIdT queue) {
595-
ReadLockT Lock{MGraphLock, std::defer_lock};
595+
ReadLockT Lock = acquireReadLock();
596596
return MGraphBuilder.isInFusionMode(queue);
597597
}
598598

sycl/source/detail/scheduler/scheduler.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -439,7 +439,7 @@ class Scheduler {
439439
static MemObjRecord *getMemObjRecord(const Requirement *const Req);
440440

441441
void deferMemObjRelease(const std::shared_ptr<detail::SYCLMemObjI> &MemObj);
442-
442+
443443
void startFusion(QueueImplPtr Queue);
444444

445445
void cancelFusion(QueueImplPtr Queue);
@@ -499,7 +499,7 @@ class Scheduler {
499499

500500
// May lock graph with read and write modes during execution.
501501
void cleanupDeferredMemObjects(BlockingT Blocking);
502-
502+
503503
// POD struct to convey some additional information from GraphBuilder::addCG
504504
// to the Scheduler to support kernel fusion.
505505
struct GraphBuildResult {

sycl/source/handler.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,8 +147,7 @@ event handler::finalize() {
147147
}
148148
}
149149

150-
const auto &type = getType();
151-
if (type == detail::CG::Kernel && !MQueue->is_in_fusion_mode() &&
150+
if (!MQueue->is_in_fusion_mode() &&
152151
MRequirements.size() + MEvents.size() + MStreamStorage.size() == 0) {
153152
// if user does not add a new dependency to the dependency graph, i.e.
154153
// the graph is not changed, and the queue is not in fusion mode, then

sycl/unittests/scheduler/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,5 @@ add_sycl_unittest(SchedulerTests OBJECT
2222
RunOnHostIntelCG.cpp
2323
EnqueueWithDependsOnDeps.cpp
2424
AccessorDefaultCtor.cpp
25+
KernelFusion.cpp
2526
)
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
//==----------- KernelFusion.cpp - Kernel Fusion scheduler unit tests ------==//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "SchedulerTest.hpp"
10+
#include "SchedulerTestUtils.hpp"
11+
12+
#include <helpers/PiMock.hpp>
13+
#include <helpers/ScopedEnvVar.hpp>
14+
#include <helpers/TestKernel.hpp>
15+
16+
#include <vector>
17+
18+
using namespace sycl;
19+
using EventImplPtr = std::shared_ptr<detail::event_impl>;
20+
21+
template <typename T, int Dim>
22+
detail::Command *CreateTaskCommand(MockScheduler &MS,
23+
detail::QueueImplPtr DevQueue,
24+
buffer<T, Dim> &buf) {
25+
MockHandlerCustomFinalize MockCGH(DevQueue, false);
26+
27+
auto acc = buf.get_access(static_cast<sycl::handler &>(MockCGH));
28+
29+
kernel_bundle KernelBundle =
30+
sycl::get_kernel_bundle<sycl::bundle_state::input>(
31+
DevQueue->get_context());
32+
auto ExecBundle = sycl::build(KernelBundle);
33+
MockCGH.use_kernel_bundle(ExecBundle);
34+
MockCGH.single_task<TestKernel<>>([] {});
35+
36+
auto CmdGrp = MockCGH.finalize();
37+
38+
std::vector<detail::Command *> ToEnqueue;
39+
detail::Command *NewCmd = MS.addCG(std::move(CmdGrp), DevQueue, ToEnqueue);
40+
EXPECT_EQ(ToEnqueue.size(), 0u);
41+
return NewCmd;
42+
}
43+
44+
bool CheckTestExecRequirements(const platform &plt) {
45+
if (plt.is_host()) {
46+
std::cout << "Not run due to host-only environment\n";
47+
return false;
48+
}
49+
// This test only contains device image for SPIR-V capable devices.
50+
if (plt.get_backend() != sycl::backend::opencl &&
51+
plt.get_backend() != sycl::backend::ext_oneapi_level_zero) {
52+
std::cout << "Only OpenCL and Level Zero are supported for this test\n";
53+
return false;
54+
}
55+
return true;
56+
}
57+
58+
bool containsCommand(detail::Command *Cmd,
59+
std::vector<detail::Command *> &List) {
60+
return std::find(List.begin(), List.end(), Cmd) != List.end();
61+
}
62+
63+
bool dependsOnViaDep(detail::Command *Dependent, detail::Command *Dependee) {
64+
return std::find_if(Dependent->MDeps.begin(), Dependent->MDeps.end(),
65+
[=](detail::DepDesc &Desc) {
66+
return Desc.MDepCommand == Dependee;
67+
}) != Dependent->MDeps.end();
68+
}
69+
70+
bool dependsOnViaEvent(detail::Command *Dependent, detail::Command *Dependee) {
71+
auto &DepEvents = Dependent->getPreparedDepsEvents();
72+
return std::find_if(DepEvents.begin(), DepEvents.end(),
73+
[=](const EventImplPtr &Ev) {
74+
return Ev->getCommand() && Ev->getCommand() == Dependee;
75+
}) != DepEvents.end();
76+
}
77+
78+
TEST_F(SchedulerTest, CancelKernelFusion) {
79+
unittest::PiMock Mock;
80+
platform Plt = Mock.getPlatform();
81+
if (!CheckTestExecRequirements(Plt))
82+
return;
83+
84+
queue QueueDev(context(Plt), default_selector_v);
85+
MockScheduler MS;
86+
87+
detail::QueueImplPtr QueueDevImpl = detail::getSyclObjImpl(QueueDev);
88+
89+
// Test scenario: Create four memory objects (buffers) and one command for
90+
// each memory object before starting fusion. Then start fusion, again adding
91+
// one command with a requirement for each of the memory objects. Then cancel
92+
// fusion and check for correct dependencies.
93+
94+
buffer<int, 1> b1{range<1>{4}};
95+
buffer<int, 1> b2{range<1>{4}};
96+
buffer<int, 1> b3{range<1>{4}};
97+
buffer<int, 1> b4{range<1>{4}};
98+
99+
auto *nonFusionCmd1 = CreateTaskCommand(MS, QueueDevImpl, b1);
100+
auto *nonFusionCmd2 = CreateTaskCommand(MS, QueueDevImpl, b2);
101+
auto *nonFusionCmd3 = CreateTaskCommand(MS, QueueDevImpl, b3);
102+
auto *nonFusionCmd4 = CreateTaskCommand(MS, QueueDevImpl, b4);
103+
104+
MS.startFusion(QueueDevImpl);
105+
106+
auto *fusionCmd1 = CreateTaskCommand(MS, QueueDevImpl, b1);
107+
auto *fusionCmd2 = CreateTaskCommand(MS, QueueDevImpl, b2);
108+
auto *fusionCmd3 = CreateTaskCommand(MS, QueueDevImpl, b3);
109+
auto *fusionCmd4 = CreateTaskCommand(MS, QueueDevImpl, b4);
110+
111+
std::vector<detail::Command *> ToEnqueue;
112+
MS.cancelFusion(QueueDevImpl, ToEnqueue);
113+
114+
// The list of commands filled by cancelFusion should contain the four
115+
// commands submitted while in fusion mode, plus the placeholder command.
116+
EXPECT_EQ(ToEnqueue.size(), 5u);
117+
EXPECT_TRUE(containsCommand(fusionCmd1, ToEnqueue));
118+
EXPECT_TRUE(containsCommand(fusionCmd2, ToEnqueue));
119+
EXPECT_TRUE(containsCommand(fusionCmd3, ToEnqueue));
120+
EXPECT_TRUE(containsCommand(fusionCmd4, ToEnqueue));
121+
122+
// Each of the commands submitted while in fusion mode should have exactly one
123+
// dependency on the command not participating in fusion, but accessing the
124+
// same memory object.
125+
EXPECT_TRUE(dependsOnViaDep(fusionCmd1, nonFusionCmd1));
126+
EXPECT_EQ(fusionCmd1->MDeps.size(), 1u);
127+
EXPECT_TRUE(dependsOnViaDep(fusionCmd2, nonFusionCmd2));
128+
EXPECT_EQ(fusionCmd2->MDeps.size(), 1u);
129+
EXPECT_TRUE(dependsOnViaDep(fusionCmd3, nonFusionCmd3));
130+
EXPECT_EQ(fusionCmd3->MDeps.size(), 1u);
131+
EXPECT_TRUE(dependsOnViaDep(fusionCmd4, nonFusionCmd4));
132+
EXPECT_EQ(fusionCmd4->MDeps.size(), 1u);
133+
134+
// There should be one placeholder command in the command list.
135+
auto FusionCmdIt = std::find_if(
136+
ToEnqueue.begin(), ToEnqueue.end(), [](detail::Command *Cmd) {
137+
return Cmd->getType() == sycl::_V1::detail::Command::FUSION;
138+
});
139+
EXPECT_NE(FusionCmdIt, ToEnqueue.end());
140+
141+
// Check that the placeholder command has an event dependency on each of the
142+
// commands submitted while in fusion mode.
143+
auto *placeHolderCmd =
144+
static_cast<detail::KernelFusionCommand *>(*FusionCmdIt);
145+
EXPECT_EQ(placeHolderCmd->getPreparedDepsEvents().size(), 4u);
146+
EXPECT_TRUE(dependsOnViaEvent(placeHolderCmd, fusionCmd2));
147+
EXPECT_TRUE(dependsOnViaEvent(placeHolderCmd, fusionCmd3));
148+
EXPECT_TRUE(dependsOnViaEvent(placeHolderCmd, fusionCmd4));
149+
EXPECT_TRUE(dependsOnViaEvent(placeHolderCmd, fusionCmd1));
150+
}

sycl/unittests/scheduler/SchedulerTestUtils.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,11 @@ class MockScheduler : public sycl::detail::Scheduler {
202202
return MGraphBuilder.addCG(std::move(CommandGroup), Queue, ToEnqueue)
203203
.NewCmd;
204204
}
205+
206+
void cancelFusion(sycl::detail::QueueImplPtr Queue,
207+
std::vector<sycl::detail::Command *> &ToEnqueue) {
208+
MGraphBuilder.cancelFusion(Queue, ToEnqueue);
209+
}
205210
};
206211

207212
void addEdge(sycl::detail::Command *User, sycl::detail::Command *Dep,

0 commit comments

Comments
 (0)