-
Notifications
You must be signed in to change notification settings - Fork 797
[SYCL][Doc] Add kernel_function lambda wrapper #17633
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
base: sycl
Are you sure you want to change the base?
Changes from 2 commits
f6581d6
82de663
be618af
3d580ff
160fff7
1743542
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -287,16 +287,17 @@ by the property, the implementation must throw a synchronous exception with the | |||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
|=== | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
== Embedding Properties into a Kernel | ||||||||||||||||||||||||||||||||||||||||||||||
=== Embedding Properties into a Kernel | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
In other situations it may be useful to embed a kernel's properties directly | ||||||||||||||||||||||||||||||||||||||||||||||
into its type, to ensure that a kernel cannot be launched without a property | ||||||||||||||||||||||||||||||||||||||||||||||
that it depends upon for correctness. | ||||||||||||||||||||||||||||||||||||||||||||||
A kernel's properties are embedded directly into its type, to ensure that a | ||||||||||||||||||||||||||||||||||||||||||||||
kernel cannot be launched without a property that it depends upon for | ||||||||||||||||||||||||||||||||||||||||||||||
correctness. | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
To enable this use-case, this extension adds a mechanism for implementations to | ||||||||||||||||||||||||||||||||||||||||||||||
extract a property list from a kernel functor, if a kernel functor declares | ||||||||||||||||||||||||||||||||||||||||||||||
a member function named `get` accepting a `sycl::ext::oneapi::experimental::properties_tag` | ||||||||||||||||||||||||||||||||||||||||||||||
tag type and returning an instance of `sycl::ext::oneapi::experimental::properties`. | ||||||||||||||||||||||||||||||||||||||||||||||
To enable this, this extension adds a mechanism for implementations to extract | ||||||||||||||||||||||||||||||||||||||||||||||
a property list from a kernel functor, if a kernel functor declares a member | ||||||||||||||||||||||||||||||||||||||||||||||
function named `get` accepting a | ||||||||||||||||||||||||||||||||||||||||||||||
`sycl::ext::oneapi::experimental::properties_tag` tag type and returning an | ||||||||||||||||||||||||||||||||||||||||||||||
instance of `sycl::ext::oneapi::experimental::properties`. | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
```c++ | ||||||||||||||||||||||||||||||||||||||||||||||
namespace sycl { | ||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -323,8 +324,8 @@ attributes to be applied to different call operators within the same | |||||||||||||||||||||||||||||||||||||||||||||
functor. An embedded property list applies to all call operators in | ||||||||||||||||||||||||||||||||||||||||||||||
the functor. | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
The example below shows how the kernel from the previous section could be | ||||||||||||||||||||||||||||||||||||||||||||||
rewritten to leverage an embedded property list: | ||||||||||||||||||||||||||||||||||||||||||||||
The example below shows how a simple vector addition kernel could be | ||||||||||||||||||||||||||||||||||||||||||||||
written to leverage an embedded property list: | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
```c++ | ||||||||||||||||||||||||||||||||||||||||||||||
struct KernelFunctor { | ||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -363,6 +364,62 @@ diagnostic; invalid combinations that can only be detected at run-time should | |||||||||||||||||||||||||||||||||||||||||||||
result in an implementation throwing an `exception` with the `errc::invalid` | ||||||||||||||||||||||||||||||||||||||||||||||
error code. | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
=== Using Properties with Lambda Expressions | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
When a SYCL kernel is defined via a lambda expression, there is no way to | ||||||||||||||||||||||||||||||||||||||||||||||
define a `get` member function and subsequently no way to embed kernel | ||||||||||||||||||||||||||||||||||||||||||||||
properties. Instead, developers must wrap the lambda expression in an object. | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
To simplify this usage pattern, this extension defines a `kernel_function` | ||||||||||||||||||||||||||||||||||||||||||||||
that encapsulates a kernel function (which may be a lambda expression) and a | ||||||||||||||||||||||||||||||||||||||||||||||
property list. | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
NOTE: Developers are free to extend `kernel_function` or define their own | ||||||||||||||||||||||||||||||||||||||||||||||
wrapper classes (e.g., to attach commonly used property lists). | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
```c++ | ||||||||||||||||||||||||||||||||||||||||||||||
namespace sycl::ext::oneapi::experimental { | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
template <typename Function, typename Properties = empty_properties_t> | ||||||||||||||||||||||||||||||||||||||||||||||
struct kernel_function { | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
kernel_function(Function f, Properties p = syclx::properties{}); | ||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
And then There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added in 3d580ff. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Okay, adding We want to support both uses below. What syntax do we need? auto lambda = [=]() {};
auto kernel = syclx::kernel_function(lambda); // lambda is an l-value
auto kernel = syclx::kernel_function([=]() {}); // lambda is an r-value There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. https://godbolt.org/z/fK36njGse works, but I don't know if that's the correct/idiomatic way. Otherwise, two overloads work too. |
||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
// Available only if Function is invocable with Args | ||||||||||||||||||||||||||||||||||||||||||||||
template <typename... Args> | ||||||||||||||||||||||||||||||||||||||||||||||
void operator()(Args... args) const { | ||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
I'm not sure about There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added in 3d580ff. |
||||||||||||||||||||||||||||||||||||||||||||||
f(std::forward<Args>(args)...); | ||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
auto get(syclx::properties_tag) { | ||||||||||||||||||||||||||||||||||||||||||||||
return Properties{}; | ||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IMO, we should be storing properties object, in case it will contain runtime properties in future. I think we also need to have two version of this - one There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The reason I didn't do this is that anything stored in the function object has to be transferred to the device as one of the kernel arguments. I think it would only make sense to allow this if the run-time properties were intended to be consumed within the kernel. We'd also have to add an explicit specialization for the case where the property list is empty or contains only compile-time properties, to avoid transferring 1 byte unnecessarily. But perhaps this is a quality of implementation thing.
Can you say more about this? Why would we only want it to be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
llvm/sycl/include/sycl/ext/oneapi/properties/properties.hpp Lines 256 to 273 in 5c5954e
I'm not sure if we have dedicated traits in the properties extension though. Maybe we can workaround that by using plain English in constraints/requirements? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Maybe we could This does sound like an issue in the "base" part of the extension though. IMO, having a "getter" for the properties without one for the actual kernel is the root cause of this issue. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've tried to address this in be618af; I've removed the implementation details from the synopsis, so that the implementation is no longer normative, and tried to describe the constraints we want. The synopsis no longer shows what members are stored inside the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why are we making accommodations for runtime kernel properties? Aren't kernel properties always compile-time by their very nature? If you think you want a runtime kernel property, then you must really want a launch property (whose value can change each time a kernel is launched). We already have a way to specify launch properties whose values are defined at runtime. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we can make either work. I thought compile-time only properties made sense, but several people have asked about run-time properties now. If we restrict this to compile-time properties only, then we're saying that the only properties that can be attached to a kernel function are properties that affect the actual compilation of the kernel. There'd be no room for properties that were "properties of the kernel" rather than "properties of how the kernel should be launched", and we couldn't change this later. I'm happy to stick with compile-time only if everybody is confident that we'll never need run-time properties, or that we'll always be able to express run-time properties as a property of the launch in a way that makes sense semantically. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We added kernel properties as a replacement for the C++ attribute decorations we have in SYCL 2020. C++ attributes are always a compile-time thing, so we do not need runtime properties to achieve our original goal. I'm having a hard time imagining a runtime kernel property. It seems like you could either use a launch property (if it affected the way the kernel was launched) or just pass a kernel argument (if it changed the way the kernel runs). Do you have an example of a runtime kernel property that wouldn't fit into either of these categories? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't have any run-time kernel properties in mind, no. @aelovikov-intel - Do you have any in mind? If nobody has any suggestions for run-time kernel properties... Since the current plan is to do this via two different overloads, could we just remove the non- It would still make sense to have the Constraint that the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Trunk version has llvm/sycl/doc/extensions/experimental/sycl_ext_oneapi_kernel_properties.asciidoc Lines 341 to 344 in a123a74
If only compile-time properties are accepted, the extension needs to be consistent throughout. Also, if compile-time only, why static getter? Can't we just have an optional type alias? That will always be compile-time only...
|
||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
private: | ||||||||||||||||||||||||||||||||||||||||||||||
const Function f; // exposition only | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
} // namespace sycl::ext::oneapi::experimental | ||||||||||||||||||||||||||||||||||||||||||||||
``` | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
The example below shows how the `KernelFunctor` example from the previous | ||||||||||||||||||||||||||||||||||||||||||||||
section can be written using this wrapper: | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
```c++ | ||||||||||||||||||||||||||||||||||||||||||||||
namespace syclx = sycl::ext::oneapi::experimental; | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
... | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
auto lambda = [=](id<1> i) const { | ||||||||||||||||||||||||||||||||||||||||||||||
a[i] = b[i] + c[i]; | ||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||
auto props = syclx::properties{syclx::work_group_size<8, 8>, syclx::sub_group_size<8>}; | ||||||||||||||||||||||||||||||||||||||||||||||
auto kernel = syclx::kernel_function(lambda, props); | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
... | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
q.parallel_for(range<2>{16, 16}, kernel).wait(); | ||||||||||||||||||||||||||||||||||||||||||||||
``` | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
=== Querying Properties in a Compiled Kernel | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
Any properties embedded into a kernel type via a property list are reflected | ||||||||||||||||||||||||||||||||||||||||||||||
|
Uh oh!
There was an error while loading. Please reload this page.