Skip to content
This repository has been archived by the owner on Jan 26, 2024. It is now read-only.

Updated spec constant proposal to use NTTP. #1

Open
wants to merge 8 commits into
base: main
Choose a base branch
from

Conversation

Naghasan
Copy link
Member

@Naghasan Naghasan commented Dec 4, 2019

This patch extended the module proposal to add better support for spec constant.
It also introduce a different way to name spec constant values.
Spec constant are named by creating an object of type spec_id and can be manipulated using NTTP.

From the SYCL compiler's perspective (C++ compiler) the value is understood to be a runtime value and
for this reason it is not possible to use such value in a `constexpr`.

### Retrieving a spec constant placeholder

Choose a reason for hiding this comment

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

What happens for a program such as:

spec_id<int> id;
queue.submit([&](handler h) {
    spec_constant<int, id> c1 = h.set_spec_constant<int, id>(1);
    spec_constant<int, id> c2 = h.set_spec_constant<int, id>(2);
    h.single_task([=]{ 
        int v = c1.get_spec_constant();
    });
});

Is this a valid program? What's the value of v? Is it 1 if native_spec_constant() is false and 2 if native_spec_constant() is true?

Copy link
Member Author

Choose a reason for hiding this comment

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

Is this a valid program?

I think this should be banned, this look very odd and may induce unnecessary complexity on runtimes (whether there is native support or not). I'll clarify this.

Is it 1 if native_spec_constant() is false and 2 if native_spec_constant() is true?

In all cases the result of native_spec_constant() does not affect the behavior.

Choose a reason for hiding this comment

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

What do you mean with ban? UB or require compiler error? I think the first isn't reasonable and the 2nd isn't possible.

Copy link
Member Author

Choose a reason for hiding this comment

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

What do you mean with ban?

Runtime exception.

UB or require compiler error? I think the first isn't reasonable and the 2nd isn't possible.

i agree with you, I don't see why we would have an UB here and a compiler error is not always possible for this.

Copy link
Member

Choose a reason for hiding this comment

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

A runtime exception looks like a good approach to me too.
Note this behaviour is already stated on https://github.com/KhronosGroup/SYCL-Shared/pull/1/files#diff-dd15ce97b1565238f2c026fc84b30239R918 even with a more restrictive approach.

This patch extended the module proposal to add better support for spec constant.
It also introduce a different way to name spec constant values.
Spec constant are named by creating an object of type `spec_id` and can be manipulated using NTTP.
…ec_constant per spec_id and per command group scope
Copy link
Member

@keryell keryell left a comment

Choose a reason for hiding this comment

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

Great proposal!
A few nitpicks and some comments.
Please clarify the PR title.

proposals/sycl_modules.md Outdated Show resolved Hide resolved
proposals/sycl_modules.md Outdated Show resolved Hide resolved
proposals/sycl_modules.md Outdated Show resolved Hide resolved
template <typename T, typename ID = T>
class spec_constant {
template <typename T>
class spec_id {
Copy link
Member

Choose a reason for hiding this comment

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

Which is obviously 1.2.1 or 2020, right ?
The problem is that it is a niche concept, so it should not recycle some well known abbreviations...
What about just

 class specialization_constant_id {

instead? Since it is just to declare some id, it should probably fit on 1 line anyway.

Copy link
Member Author

Choose a reason for hiding this comment

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

I reused the SPIR-V naming convention (OpSpecConstant) as in-fine this is to allow its representation in SYCL.

I'm fine to change spec to specialization. I don't really see a need, but you are in a better position to say if this makes things easier to understand.

Copy link
Member Author

Choose a reason for hiding this comment

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

I made the change in this commit, ad02463

I'm not convince we gain anything with this extra verbosity though.

Copy link
Member

Choose a reason for hiding this comment

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

We gain a clarification between specification and specialization confusion.
Since this is a niche use case, I do not think it will jeopardize the programmer productivity but will help the reader not familiar with SPIR-V details...

Copy link
Member Author

@Naghasan Naghasan Jan 9, 2020

Choose a reason for hiding this comment

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

will help the reader not familiar with SPIR-V details

Maybe I should stop following SPIR-V then :)

If you feel it is better, we'll stick to it, no strong opinion on this

proposals/sycl_modules.md Outdated Show resolved Hide resolved
proposals/sycl_modules.md Outdated Show resolved Hide resolved
From the SYCL compiler's perspective (C++ compiler) the value is understood to be a runtime value and
for this reason it is not possible to use such value in a `constexpr`.

### Retrieving a spec constant placeholder
Copy link
Member

Choose a reason for hiding this comment

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

A runtime exception looks like a good approach to me too.
Note this behaviour is already stated on https://github.com/KhronosGroup/SYCL-Shared/pull/1/files#diff-dd15ce97b1565238f2c026fc84b30239R918 even with a more restrictive approach.

- Getting the placeholder for a previously set specialization constant value;
- If no specialization constant where ever set, then the *placeholder* object holds the default value of its `spec_id`.
- Setting a specialization constant value and retrieving the placeholder
- Performance notice: this function may cause the SYCL runtime to implicitly recompile the relevant SYCL module with the given value. This may have an important effect on performances and users should check their vendor recommendations on how to efficiently uses this functionality.
Copy link
Member

Choose a reason for hiding this comment

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

Just think FPGA... ;-) But on the other hand an implementation could cache all the previous instances too... So it really depends on the use case.

Copy link
Member Author

Choose a reason for hiding this comment

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

You can say you don't support the feature, or rely on a similar strategy as the host one.

I added this note it may not be obvious what is happening under the hood. I expect implementations to cache generated kernels, but if the user changes the value too often, there is nothing the runtime can really do and this will create a significant slowdown.

As this function is the most convenient one, I think it is important to mention it.

proposals/sycl_modules.md Outdated Show resolved Hide resolved
proposals/sycl_modules.md Outdated Show resolved Hide resolved
v 0.10:

* Rework of the specialization constant proposal
* Add `specialization _id` and `specialization _constant` definitions
Copy link
Member

Choose a reason for hiding this comment

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

Curious spaces before _

Copy link
Member Author

Choose a reason for hiding this comment

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

that's odd,

anyway, now fixed

// Available if module_status::executable
// Retrieve the value of the specialization constant associated with this module.
// Call only valid from the host.
template<class T, specialization_id<T>& s>
Copy link
Member

Choose a reason for hiding this comment

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

After a second review I have just realized the & here, meaning that the id is actually the address of the specialization_id object...
This changes quite a lot in my understanding of the design...


// Available if module_status::executable
// Only from C++17
// Set the value of the specialization constant.
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
// Set the value of the specialization constant.
// Get the value of the specialization constant.

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed

// Call only valid from the host.
template<auto& s>
specialization_constant<typename std::remove_reference_t<decltype(s)>::type, s>
get_specialization_constant(handler&);
Copy link
Member

Choose a reason for hiding this comment

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

While waiting for concepts to be mandatory in SYCL, at least you should specificy in the comment that s needs to be an existing specialization identier or it will throw. Also that the specialization constant has been previously initialized.


// Available if module_status::executable
// Retrieve the value of the specialization constant associated with this module.
// Call only valid from the host.
Copy link
Member

Choose a reason for hiding this comment

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

Also that the specialization constant has been previously initialized or it will throw.

`specialization_id` is used by the *SYCL implementation* to *reference* constant whose
concrete value will only be known during the application execution.
To create a new `specialization_id` in a module, the user creates a `specialization_id` object with an *automatic* or *static* storage duration in the namespace or class scope.
The user then uses references to this object to bind the `specialization_constant` value to the *identifier*.
Copy link
Member

Choose a reason for hiding this comment

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

I do not think it binds the specialization_constant value to the identifier.
If I understand correctly it binds the specialization_constant value to the identifier and the command-group handler.
So a single specialization_id` object can be used to define different constant in different kernels.

To create a new `specialization_id` in a module, the user creates a `specialization_id` object with an *automatic* or *static* storage duration in the namespace or class scope.
The user then uses references to this object to bind the `specialization_constant` value to the *identifier*.

`specialization_id` objects must be forward declarable and cannot be moved or copied.
Copy link
Member

Choose a reason for hiding this comment

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

Now that I understand that you are interested in the address of the object and not the value of the object, I need to think about how it works with extern and multiple translation units.
The object could be moved or copied but at the end it is just another object with a different address, so it represents another specialization_id.
Also since you are interested by the address, why do you care about "objects must be forward declarable"?
This address mechanism is different from the type approach used to name kernels and global pipes in Intel extension. Is it really simpler?

Copy link
Member

@keryell keryell Jan 8, 2020

Choose a reason for hiding this comment

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

After playing with this, it looks like the specialization_id objects needs static storage.
But by looking at the C++20 draft, I cannot see this restriction in 13.4.2 Template non-type arguments [temp.arg.nontype]

Some experiment in https://github.com/keryell/teaching_C-plus-plus/blob/master/NTTP_ref.cpp and https://github.com/keryell/teaching_C-plus-plus/blob/master/NTTP_ref_2.cpp

// This allow the user to setup a default value for the specialization_id instance.
// The initialization of T must be evaluated at compile time to be valid.
template<class... Args >
explicit constexpr specialization_id(Args&&...);

Choose a reason for hiding this comment

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

Why does this take a parameter pack rather than an argument of type T?

@keryell
Copy link
Member

keryell commented Feb 24, 2020

@hfinkel gave a presentation in Prague during ISO C++ SG7: P1609R3 "C++ Should Support Just-in-Time Compilation" http://wg21.link/p1609 which reminded me this discussion. cplusplus/papers#388

At least his paper adds a new C++ paradigm that might be similar to this concept of specialization constant, from a C++ perspective. It could give to specialization constant a kind of true C++ citizenship...
But from what I understand, what is possible to do at the SPIR-V level is quite limited compared to generic template instantiation envisioned by Hal Finkel.

This new proposal remove the need to use `spec_constant` object and
uses a kernel handler instead to retrieve the values.

Signed-off-by: Victor Lomuller <[email protected]>
@AerialMantis
Copy link
Collaborator

Whilst writing the wording for the proposal I have made some observations/alterations:

  • I propose that we move the member function native_specialization_constant to the device_image interface, as a module can contain multiple device_images and some may support native specialization constants and some may not. This change has not yet been made in the wording MR.
  • As we are now moving to C++17 for SYCL 2020 I have only introduced the post C++17 overloads of has/set/get_specialization_constant.
  • I have renamed the member alias of specialization_id type to value_type to align with the C++ convention.
  • I have renamed use_specialization_constant to contains_specialization_constants, as the former was very similar in name to the other member functions so suggested that the member function would instruct the module to use specialization constants.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants