Skip to content
This repository was archived by the owner on Mar 28, 2023. It is now read-only.

[SYCL] add host-task-dependency test #46

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
191 changes: 191 additions & 0 deletions SYCL/Basic/host-task-dependency.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
// RUN: %clangxx -fsycl -fsycl-targets=%sycl_triple %s -o %t.out %threads_lib
// RUN: %CPU_RUN_PLACEHOLDER SYCL_PI_TRACE=-1 %t.out 2>&1 %CPU_CHECK_PLACEHOLDER
// RUN: %GPU_RUN_PLACEHOLDER SYCL_PI_TRACE=-1 %t.out 2>&1 %GPU_CHECK_PLACEHOLDER
// RUN: %ACC_RUN_PLACEHOLDER SYCL_PI_TRACE=-1 %t.out 2>&1 %ACC_CHECK_PLACEHOLDER
//
// TODO: Behaviour is unstable for level zero on Windows. Enable when fixed.
// UNSUPPORTED: windows && level_zero

#include <atomic>
#include <condition_variable>
#include <future>
#include <mutex>
#include <thread>

#include <CL/sycl.hpp>

namespace S = cl::sycl;

struct Context {
std::atomic_bool Flag;
S::queue &Queue;
S::buffer<int, 1> Buf1;
S::buffer<int, 1> Buf2;
S::buffer<int, 1> Buf3;
std::mutex Mutex;
std::condition_variable CV;
};

void Thread1Fn(Context *Ctx) {
// 0. initialize resulting buffer with apriori wrong result
{
S::accessor<int, 1, S::access::mode::write, S::access::target::host_buffer>
Acc(Ctx->Buf1);

for (size_t Idx = 0; Idx < Acc.get_count(); ++Idx)
Acc[Idx] = -1;
}

{
S::accessor<int, 1, S::access::mode::write, S::access::target::host_buffer>
Acc(Ctx->Buf2);

for (size_t Idx = 0; Idx < Acc.get_count(); ++Idx)
Acc[Idx] = -2;
}

{
S::accessor<int, 1, S::access::mode::write, S::access::target::host_buffer>
Acc(Ctx->Buf3);

for (size_t Idx = 0; Idx < Acc.get_count(); ++Idx)
Acc[Idx] = -3;
}

// 1. submit task writing to buffer 1
Ctx->Queue.submit([&](S::handler &CGH) {
S::accessor<int, 1, S::access::mode::write,
S::access::target::global_buffer>
GeneratorAcc(Ctx->Buf1, CGH);

auto GeneratorKernel = [GeneratorAcc] {
for (size_t Idx = 0; Idx < GeneratorAcc.get_count(); ++Idx)
GeneratorAcc[Idx] = Idx;
};

CGH.single_task<class GeneratorTask>(GeneratorKernel);
});

// 2. submit host task writing from buf 1 to buf 2
auto HostTaskEvent = Ctx->Queue.submit([&](S::handler &CGH) {
S::accessor<int, 1, S::access::mode::read, S::access::target::host_buffer>
CopierSrcAcc(Ctx->Buf1, CGH);
S::accessor<int, 1, S::access::mode::write, S::access::target::host_buffer>
CopierDstAcc(Ctx->Buf2, CGH);

auto CopierHostTask = [CopierSrcAcc, CopierDstAcc, &Ctx] {
for (size_t Idx = 0; Idx < CopierDstAcc.get_count(); ++Idx)
CopierDstAcc[Idx] = CopierSrcAcc[Idx];

bool Expected = false;
bool Desired = true;
assert(Ctx->Flag.compare_exchange_strong(Expected, Desired));

{
std::lock_guard<std::mutex> Lock(Ctx->Mutex);
Ctx->CV.notify_all();
}
};

CGH.codeplay_host_task(CopierHostTask);
});

// 3. submit simple task to move data between two buffers
Ctx->Queue.submit([&](S::handler &CGH) {
S::accessor<int, 1, S::access::mode::read, S::access::target::global_buffer>
SrcAcc(Ctx->Buf2, CGH);
S::accessor<int, 1, S::access::mode::write,
S::access::target::global_buffer>
DstAcc(Ctx->Buf3, CGH);

CGH.depends_on(HostTaskEvent);

auto CopierKernel = [SrcAcc, DstAcc] {
for (size_t Idx = 0; Idx < DstAcc.get_count(); ++Idx)
DstAcc[Idx] = SrcAcc[Idx];
};

CGH.single_task<class CopierTask>(CopierKernel);
});

// 4. check data in buffer #3
{
S::accessor<int, 1, S::access::mode::read, S::access::target::host_buffer>
Acc(Ctx->Buf3);

bool Failure = false;

for (size_t Idx = 0; Idx < Acc.get_count(); ++Idx) {
fprintf(stderr, "Third buffer [%3zu] = %i\n", Idx, Acc[Idx]);

Failure |= (Acc[Idx] != Idx);
}

assert(!Failure && "Invalid data in third buffer");
}
}

void Thread2Fn(Context *Ctx) {
std::unique_lock<std::mutex> Lock(Ctx->Mutex);

// T2.1. Wait until flag F is set eq true.
Ctx->CV.wait(Lock, [Ctx] { return Ctx->Flag.load(); });

assert(Ctx->Flag.load());
}

void test() {
auto EH = [](S::exception_list EL) {
for (const std::exception_ptr &E : EL) {
throw E;
}
};

S::queue Queue(EH);

Context Ctx{{false}, Queue, {10}, {10}, {10}, {}, {}};

// 0. setup: thread 1 T1: exec smth; thread 2 T2: waits; init flag F = false
auto A1 = std::async(std::launch::async, Thread1Fn, &Ctx);
auto A2 = std::async(std::launch::async, Thread2Fn, &Ctx);

A1.get();
A2.get();

assert(Ctx.Flag.load());

// 3. check via host accessor that buf 2 contains valid data
{
S::accessor<int, 1, S::access::mode::read, S::access::target::host_buffer>
ResultAcc(Ctx.Buf2);

bool Failure = false;
for (size_t Idx = 0; Idx < ResultAcc.get_count(); ++Idx) {
fprintf(stderr, "Second buffer [%3zu] = %i\n", Idx, ResultAcc[Idx]);

Failure |= (ResultAcc[Idx] != Idx);
}

assert(!Failure && "Invalid data in result buffer");
}
}

int main() {
test();

return 0;
}

// launch of GeneratorTask kernel
// CHECK:---> piKernelCreate(
// CHECK: GeneratorTask
// CHECK:---> piEnqueueKernelLaunch(
// prepare for host task
// CHECK:---> piEnqueueMemBufferMap(
// launch of CopierTask kernel
// CHECK:---> piKernelCreate(
// CHECK: CopierTask
// CHECK:---> piEnqueueKernelLaunch(
// TODO need to check for piEventsWait as "wait on dependencies of host task".
// At the same time this piEventsWait may occur anywhere after
// piEnqueueMemBufferMap ("prepare for host task").
3 changes: 3 additions & 0 deletions SYCL/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ if( NOT OpenCL_LIBRARY )
find_package(OpenCL)
endif()

find_package(Threads REQUIRED)
set(SYCL_THREADS_LIB ${CMAKE_THREAD_LIBS_INIT})

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should also propagate the value in lit.cfg.py and lit.site.cfg.cpy.in.
Please verify your change before submition

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The value is already there. I checked it again.
https://github.com/intel/llvm-test-suite/blob/intel/SYCL/lit.cfg.py#L113
https://github.com/intel/llvm-test-suite/blob/intel/SYCL/lit.site.cfg.py.in#L25

So only changes in SYCL/CMakeLists.txt are needed for SYCL_THREADS_LIB to work with threads.


configure_file("${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.py.in" "${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg")

if(CHECK_SYCL_ALL)
Expand Down