Skip to content
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

Should accessors created from a command group handler throw if passed empty buffers? #408

Closed
maarquitos14 opened this issue Apr 28, 2023 · 6 comments · Fixed by #434
Closed

Comments

@maarquitos14
Copy link
Contributor

According to Table 131, require throws an exception if (acc.empty() == true). However, another way of creating a requirement is directly creating an accessor from a command group handler, as described in 4.9.4.1:

When an accessor is created from a [command group handler](https://registry.khronos.org/SYCL/specs/sycl-2020/html/sycl-2020.html#handler), a requirement is implicitly added to the [command group](https://registry.khronos.org/SYCL/specs/sycl-2020/html/sycl-2020.html#command-group) for the accessor’s data.

If an accessor created from a command group handler is passed and empty buffer, should it throw an exception too?

@tomdeakin
Copy link
Contributor

  • Does empty buffer mean a buffer of size zero?
  • 4.7. says zero-sized buffers are allowed.
  • Also 3.10 "The minimum number of elements in a buffer object is zero."
  • There are valid use cases for this. Should require not throw?

@maarquitos14
Copy link
Contributor Author

There's several cases here that we should consider:

    1. Accessor created using default constructor (there's no buffer at all) used in a call to require(). I can see why this is set to throw, since calling require means "gaining access to the given memory object before executing the kernel", but since there's no buffer, what is the memory object?
    1. Accessor created with an empty buffer (i.e. buffer of size zero) used in a call to require(). Currently, this is also set to throw because acc.empty() == true. One could argue that this one actually has a memory object --the buffer-- even if it is empty, so the requirement could be successful here.
    1. Accessor created with an empty buffer and a command group handler (i.e. with an implicit requirement). Currently, this is not set to throw, even though acc.empty() == true and having a (implicit) requirement. Again, one could argue that this one actually has a memory object even if it is empty.

I think that cases 2. and 3. should have the same behavior, either both throw or none throws. Ideally, the three cases should have the same behavior. However, I don't see an easy way to differentiate when an accessor is empty because the buffer is empty, or because there is no buffer at all (default constructor).

@gmlueck
Copy link
Contributor

gmlueck commented May 15, 2023

My guess is that we intended to say that handler::require throws an exception if the accessor is default-constructed. (Not all cases of an empty accessor.) This would be consistent with this wording in the description of the default accessor constructor:

A default constructed accessor can be passed to a SYCL kernel function but it is not valid to register it with the command group handler.

Is it hard for the implementation to differentiate between a default-constructed accessor and other empty accessors?

@maarquitos14
Copy link
Contributor Author

Is it hard for the implementation to differentiate between a default-constructed accessor and other empty accessors?

I don't think it is, no. Worst case scenario would be having to add a bool to mark default-constructed cases, but probably we already have enough information to differentiate.

@gmlueck
Copy link
Contributor

gmlueck commented May 17, 2023

Thinking about the default-constructed accessor some more, it seems weird that we say "A default constructed accessor can be passed to a SYCL kernel function":

A default constructed accessor can be passed to a SYCL kernel function but it is not valid to register it with the command group handler.

Since a default-constructed accessor isn't associated with a handler, it seems like it should not be possible to pass it as a kernel argument.

gmlueck added a commit to gmlueck/SYCL-Docs that referenced this issue Jun 23, 2023
The previous wording caused some confusion about whether a default
constructed `accessor` is a placeholder.  In general, the accessor
constructors that do not take a `handler` parameter construct
placeholder accessors, which could give the impression that the
default constructed accessor is a placeholder.  Clarify this by
specifically stating that the default constructed accessor is not a
placeholder and that it may be passed as a kernel parameter without
calling `handler::require`.

The default constructed `accessor` and the default constructed
`local_accessor` are similar because they can both be passed as
kernel parameters and neither allocates any memory for use in the
kernel.  Adjust the wording for these two constructors to be more
similar.

Remove wording from `handler::require` saying that it is an error to
call this function for a zero sized accessor.  We allow non-placeholder
accessor that are zero sized, so it makes no sense to disallow this for
placeholder accessors.

Remove wording from the default `accessor` constructor saying that it
is an error to pass such an accessor to `handler::require`.  We allow
other non-placeholder accessors to be passed to `handler::require`, so
it makes sense to allow this for the default constructed accessor too.

Clarify the wording for `accessor::is_placeholder`.  The previous
wording "if the accessor was constructed as a placeholder" caused
confusion.  What if an accessor was constructed as a placeholder and
then later assigned to a non-placeholder accessor (or swapped to a
non-placeholder accessor)?  Presumably, we want `is_placeholder` to
return `false` in such a case.  The new wording removes this confusing
phrasing.

Closes KhronosGroup#408.
@gmlueck
Copy link
Contributor

gmlueck commented Jun 23, 2023

Here's how I think we should resolve these issues:

  • I think it was a mistake to say that handler::require throws an exception if the accessor has zero size. As noted above, it's valid to create non-placeholder accessors with zero size, so I don't see why it should be an error to create a zero sized placeholder accessor. Therefore, I think we should remove the language in handler::require about throwing an exception for a zero-sized accessor.

  • I think we should clarify that a default constructed accessor is not a placeholder. The current spec wording does not say that it is a placeholder, so I think this position is supported even by the current spec. As a result it is valid to pass a default constructed accessor as a kernel parameter even without calling handler::require. Again, this is supported even by the current spec wording, which says that a default constructed accessor can be passed as a kernel parameter. Note also that a default constructed local_accessor can be passed as a kernel parameter, so it makes sense that the two should be consistent.

  • I do not know why we said that it is an error to call handler::require for a default constructed accessor. It is valid to call handler::require for other non-placeholder accessor, so it seems like the default constructed accessor should be no different. In these cases, calling handler::require has no effect. Therefore, I think we should remove wording about this being an error.

See my proposed wording in #434.

keryell added a commit that referenced this issue Jul 20, 2023
Clarify default constructed accessor and "require".

The previous wording caused some confusion about whether a default constructed accessor is a placeholder. In general, the accessor constructors that do not take a handler parameter construct placeholder accessors, which could give the impression that the default constructed accessor is a placeholder. Clarify this by specifically stating that the default constructed accessor is not a placeholder and that it may be passed as a kernel parameter without calling handler::require.

The default constructed accessor and the default constructed local_accessor are similar because they can both be passed as kernel parameters and neither allocates any memory for use in the kernel. Adjust the wording for these two constructors to be more similar.

Remove wording from handler::require saying that it is an error to call this function for a zero sized accessor. We allow non-placeholder accessor that are zero sized, so it makes no sense to disallow this for placeholder accessors.

Remove wording from the default accessor constructor saying that it is an error to pass such an accessor to handler::require. We allow other non-placeholder accessors to be passed to handler::require, so it makes sense to allow this for the default constructed accessor too.

Clarify the wording for accessor::is_placeholder. The previous wording "if the accessor was constructed as a placeholder" caused confusion. What if an accessor was constructed as a placeholder and then later assigned to a non-placeholder accessor (or swapped to a non-placeholder accessor)? Presumably, we want is_placeholder to return false in such a case. The new wording removes this confusing phrasing.

Closes #408.
steffenlarsen added a commit to steffenlarsen/SYCL-CTS that referenced this issue Aug 2, 2023
Following the resolution of
KhronosGroup/SYCL-Docs#408 with
KhronosGroup/SYCL-Docs#434 DPC++ has been
changed to not throw for empty accessors. As such, the test cases using
check_zero_length_buffer_constructor can now be enabled.

Signed-off-by: Larsen, Steffen <steffen.larsen@intel.com>
keryell added a commit that referenced this issue Sep 10, 2024
Clarify default constructed accessor and "require".

The previous wording caused some confusion about whether a default constructed accessor is a placeholder. In general, the accessor constructors that do not take a handler parameter construct placeholder accessors, which could give the impression that the default constructed accessor is a placeholder. Clarify this by specifically stating that the default constructed accessor is not a placeholder and that it may be passed as a kernel parameter without calling handler::require.

The default constructed accessor and the default constructed local_accessor are similar because they can both be passed as kernel parameters and neither allocates any memory for use in the kernel. Adjust the wording for these two constructors to be more similar.

Remove wording from handler::require saying that it is an error to call this function for a zero sized accessor. We allow non-placeholder accessor that are zero sized, so it makes no sense to disallow this for placeholder accessors.

Remove wording from the default accessor constructor saying that it is an error to pass such an accessor to handler::require. We allow other non-placeholder accessors to be passed to handler::require, so it makes sense to allow this for the default constructed accessor too.

Clarify the wording for accessor::is_placeholder. The previous wording "if the accessor was constructed as a placeholder" caused confusion. What if an accessor was constructed as a placeholder and then later assigned to a non-placeholder accessor (or swapped to a non-placeholder accessor)? Presumably, we want is_placeholder to return false in such a case. The new wording removes this confusing phrasing.

Closes #408.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants