-
Notifications
You must be signed in to change notification settings - Fork 788
[SYCL] Add a leaf limit to the execution graph #1070
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
//==---------------- circular_buffer.hpp - Circular buffer -----------------==// | ||
// | ||
// 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 | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
#pragma once | ||
|
||
#include <CL/sycl/detail/defines.hpp> | ||
|
||
#include <deque> | ||
#include <utility> | ||
|
||
__SYCL_INLINE namespace cl { | ||
namespace sycl { | ||
namespace detail { | ||
|
||
// A partial implementation of a circular buffer: once its capacity is full, | ||
// new data overwrites the old. | ||
template <typename T> class CircularBuffer { | ||
public: | ||
explicit CircularBuffer(size_t Capacity) : MCapacity{Capacity} {}; | ||
|
||
using value_type = T; | ||
using pointer = T *; | ||
using const_pointer = const T *; | ||
using reference = T &; | ||
using const_reference = const T &; | ||
|
||
using iterator = typename std::deque<T>::iterator; | ||
using const_iterator = typename std::deque<T>::const_iterator; | ||
|
||
iterator begin() { return MValues.begin(); } | ||
|
||
const_iterator begin() const { return MValues.begin(); } | ||
|
||
iterator end() { return MValues.end(); } | ||
|
||
const_iterator end() const { return MValues.end(); } | ||
|
||
reference front() { return MValues.front(); } | ||
|
||
const_reference front() const { return MValues.front(); } | ||
|
||
reference back() { return MValues.back(); } | ||
|
||
const_reference back() const { return MValues.back(); } | ||
|
||
reference operator[](size_t Idx) { return MValues[Idx]; } | ||
|
||
const_reference operator[](size_t Idx) const { return MValues[Idx]; } | ||
|
||
size_t size() const { return MValues.size(); } | ||
|
||
size_t capacity() const { return MCapacity; } | ||
|
||
bool empty() const { return MValues.empty(); }; | ||
|
||
bool full() const { return MValues.size() == MCapacity; }; | ||
|
||
void push_back(T Val) { | ||
if (MValues.size() == MCapacity) | ||
MValues.pop_front(); | ||
MValues.push_back(std::move(Val)); | ||
} | ||
|
||
void push_front(T Val) { | ||
if (MValues.size() == MCapacity) | ||
MValues.pop_back(); | ||
MValues.push_front(std::move(Val)); | ||
} | ||
|
||
void pop_back() { MValues.pop_back(); } | ||
|
||
void pop_front() { MValues.pop_front(); } | ||
|
||
void erase(const_iterator Pos) { MValues.erase(Pos); } | ||
|
||
void erase(const_iterator First, const_iterator Last) { | ||
MValues.erase(First, Last); | ||
} | ||
|
||
void clear() { MValues.clear(); } | ||
|
||
private: | ||
// Deque is used as the underlying container for double-ended push/pop | ||
// operations and built-in iterator support. Frequent memory allocations | ||
// and deallocations are a concern, switching to an array/vector might be a | ||
// worthwhile optimization. | ||
std::deque<T> MValues; | ||
sergey-semenov marked this conversation as resolved.
Show resolved
Hide resolved
|
||
const size_t MCapacity; | ||
}; | ||
|
||
} // namespace detail | ||
} // namespace sycl | ||
} // namespace cl |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
// RUN: %clangxx -fsycl %s -o %t.out | ||
// RUN: %t.out | ||
|
||
#include <CL/sycl/detail/circular_buffer.hpp> | ||
|
||
#include <algorithm> | ||
#include <cassert> | ||
#include <vector> | ||
|
||
// This test contains basic checks for cl::sycl::detail::CircularBuffer | ||
void checkEquality(const cl::sycl::detail::CircularBuffer<int> &CB, | ||
const std::vector<int> &V) { | ||
assert(std::equal(CB.begin(), CB.end(), V.begin())); | ||
} | ||
|
||
int main() { | ||
const size_t Capacity = 6; | ||
cl::sycl::detail::CircularBuffer<int> CB{Capacity}; | ||
assert(CB.capacity() == Capacity); | ||
assert(CB.empty()); | ||
|
||
int nextValue = 0; | ||
for (; nextValue < Capacity; ++nextValue) { | ||
assert(CB.size() == nextValue); | ||
CB.push_back(nextValue); | ||
} | ||
assert(CB.full() && CB.size() == CB.capacity()); | ||
checkEquality(CB, {0, 1, 2, 3, 4, 5}); | ||
|
||
CB.push_back(nextValue++); | ||
checkEquality(CB, {1, 2, 3, 4, 5, 6}); | ||
CB.push_front(nextValue++); | ||
checkEquality(CB, {7, 1, 2, 3, 4, 5}); | ||
|
||
assert(CB.front() == 7); | ||
assert(CB.back() == 5); | ||
|
||
CB.erase(CB.begin() + 2); | ||
checkEquality(CB, {7, 1, 3, 4, 5}); | ||
CB.erase(CB.begin(), CB.begin() + 2); | ||
checkEquality(CB, {3, 4, 5}); | ||
|
||
CB.pop_back(); | ||
checkEquality(CB, {3, 4}); | ||
CB.pop_front(); | ||
checkEquality(CB, {4}); | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
// RUN: %clangxx -fsycl %s -o %t.out | ||
// RUN: %t.out | ||
#include <CL/sycl.hpp> | ||
|
||
#include <memory> | ||
#include <vector> | ||
|
||
// This test checks the leaf limit imposed on the execution graph | ||
|
||
using namespace cl::sycl; | ||
|
||
class FakeCommand : public detail::Command { | ||
public: | ||
FakeCommand(detail::QueueImplPtr Queue, detail::Requirement Req) | ||
: Command{detail::Command::ALLOCA, Queue}, MRequirement{std::move(Req)} {} | ||
|
||
void printDot(std::ostream &Stream) const override {} | ||
|
||
const detail::Requirement *getRequirement() const final { | ||
return &MRequirement; | ||
}; | ||
|
||
cl_int enqueueImp() override { return MRetVal; } | ||
|
||
cl_int MRetVal = CL_SUCCESS; | ||
|
||
protected: | ||
detail::Requirement MRequirement; | ||
}; | ||
|
||
class TestScheduler : public detail::Scheduler { | ||
public: | ||
void AddNodeToLeaves(detail::MemObjRecord *Rec, detail::Command *Cmd, | ||
access::mode Mode) { | ||
return MGraphBuilder.AddNodeToLeaves(Rec, Cmd, Mode); | ||
} | ||
|
||
detail::MemObjRecord * | ||
getOrInsertMemObjRecord(const detail::QueueImplPtr &Queue, | ||
detail::Requirement *Req) { | ||
return MGraphBuilder.getOrInsertMemObjRecord(Queue, Req); | ||
} | ||
}; | ||
|
||
int main() { | ||
TestScheduler TS; | ||
queue Queue; | ||
buffer<int, 1> Buf(range<1>(1)); | ||
detail::Requirement FakeReq{{0, 0, 0}, | ||
{0, 0, 0}, | ||
{0, 0, 0}, | ||
access::mode::read_write, | ||
detail::getSyclObjImpl(Buf).get(), | ||
0, | ||
0, | ||
0}; | ||
FakeCommand *FakeDepCmd = | ||
new FakeCommand(detail::getSyclObjImpl(Queue), FakeReq); | ||
detail::MemObjRecord *Rec = | ||
TS.getOrInsertMemObjRecord(detail::getSyclObjImpl(Queue), &FakeReq); | ||
|
||
// Create commands that will be added as leaves exceeding the limit by 1 | ||
std::vector<FakeCommand *> LeavesToAdd; | ||
for (size_t i = 0; i < Rec->MWriteLeaves.capacity() + 1; ++i) { | ||
LeavesToAdd.push_back( | ||
new FakeCommand(detail::getSyclObjImpl(Queue), FakeReq)); | ||
} | ||
// Create edges: all soon-to-be leaves are direct users of FakeDep | ||
for (auto Leaf : LeavesToAdd) { | ||
FakeDepCmd->addUser(Leaf); | ||
Leaf->addDep(detail::DepDesc{FakeDepCmd, Leaf->getRequirement(), nullptr}); | ||
} | ||
// Add edges as leaves and exceed the leaf limit | ||
for (auto LeafPtr : LeavesToAdd) { | ||
TS.AddNodeToLeaves(Rec, LeafPtr, access::mode::read_write); | ||
} | ||
// Check that the oldest leaf has been removed from the leaf list | ||
// and added as a dependency of the newest one instead | ||
const detail::CircularBuffer<detail::Command *> &Leaves = Rec->MWriteLeaves; | ||
assert(std::find(Leaves.begin(), Leaves.end(), LeavesToAdd.front()) == | ||
Leaves.end()); | ||
for (size_t i = 1; i < LeavesToAdd.size(); ++i) { | ||
assert(std::find(Leaves.begin(), Leaves.end(), LeavesToAdd[i]) != | ||
Leaves.end()); | ||
} | ||
FakeCommand *OldestLeaf = LeavesToAdd.front(); | ||
FakeCommand *NewestLeaf = LeavesToAdd.back(); | ||
assert(OldestLeaf->MUsers.size() == 1); | ||
assert(OldestLeaf->MUsers[0] == NewestLeaf); | ||
assert(NewestLeaf->MDeps.size() == 2); | ||
assert(NewestLeaf->MDeps[1].MDepCommand == OldestLeaf); | ||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.