OpenCL-Docs icon indicating copy to clipboard operation
OpenCL-Docs copied to clipboard

Clarification of clCreateBuffer memory flags

Open YurkoFlisk opened this issue 7 months ago • 2 comments

The table of memory flags describes CL_MEM_READ_WRITE like this:

This flag specifies that the memory object will be read and written by a kernel. This is the default.

It isn't clear what "the default" means in this case (see SO question where this causes confusion). I can think of two options:

  1. When flags is 0, the behaviour is as if it was CL_MEM_READ_WRITE. This is supported by an additional sentence in flags parameter description in specification version 2.2, which is, however, missing from version 3.0:

    ... If the value specified for flags is 0, the default is used which is CL_​MEM_​READ_​WRITE.

  2. Among CL_MEM_READ_WRITE, CL_MEM_READ_ONLY and CL_MEM_WRITE_ONLY, which are mutually exclusive and specify what kinds of access the kernel does to the memory object, CL_MEM_READ_WRITE is assumed by default (i.e., when all 3 corresponding bits in flags are set to 0, even if some of the other bits are set to 1). This seems more intuitive to me, because it would mean that the flags cannot specify a memory object which is inaccessible by the kernel. But, if so, why is CL_MEM_READ_WRITE a separate bit at all?

Whichever of these (or something else) is the intention, I think the wording should be clarified to reflect it.

YurkoFlisk avatar May 08 '25 00:05 YurkoFlisk

Yeah, this is confusing, but unfortunately it's been the case since OpenCL 1.0 so I'm not sure how much we can do to "fix" it at this point.

Here's my mental model:

If none of CL_MEM_READ_WRITE, CL_MEM_READ_ONLY, or CL_MEM_WRITE_ONLY are included in the flags, then the memory object is both readable and writeable in a kernel, so it is essentially the same as it would have been if the flags included CL_MEM_READ_WRITE. This is true both when flags is zero and when some other bits are set. I think this means that both of your options are true?

There aren't many places in the CTS where a buffer object is created with flags equal to zero, but I did find at least one, so this should be the case for all conformant implementations:

https://github.com/KhronosGroup/OpenCL-CTS/blob/b011fb26d9a2b5791abd84a3507a835717396cf5/test_conformance/basic/test_kernel_memory_alignment.cpp#L187

    results = clCreateBuffer(context, 0, sizeof(cl_ulong)*6, NULL, &error);
    test_error(error, "clCreateBuffer failed");

Cases where a memory object is created with nonzero flags but without specifying any accessibility flags are much more common, e.g.:

    clMemWrapper read_mem(clCreateBuffer(context, CL_MEM_USE_HOST_PTR,
                                         read_data_size, read_data, &status));

To help clear up the confusion, maybe we could:

  • Rephrase or otherwise expand "this is the default", to say a few more words similar to the explanation above.
  • Rephrase the "mutually exclusive" text. I found it confusing not to see any "mutually exclusive" text in the description for CL_MEM_READ_WRITE, and "CL_MEM_READ_WRITE or CL_MEM_WRITE_ONLY and CL_MEM_READ_ONLY are mutually exclusive" was difficult to parse (for me at least).

What do you think?

bashbaug avatar May 08 '25 03:05 bashbaug

Agree on both. The discussion is also relevant for the separate memory flags table for SVM.

There's also some asymmetry between CL_MEM_* and CL_MEM_HOST_* flags. For both, the default behaviour is read-write access (from device and host, respectively). The former group has the default behaviour (READ_WRITE) as a separate flag, the latter group doesn't, which could make it seem at a first glance that READ_WRITE host access isn't allowed. However, I think, technically there's no ambiguity, because buffer manipulation functions themselves (within possible reasons to return CL_INVALID_OPERATION) use the set CL_MEM_HOST_* flags only to restrict allowed operations, so if none is set, read-write access is implied. Still, if within clCreateBuffer or the table there's (in whatever form) a comment about default CL_MEM_* behaviour, it would be nice to have one about CL_MEM_HOST_* as well. And, while we are at it, CL_MEM_HOST_* flags also have the same difficult-to-parse phrasing for mutual exclusivity.

It should be noted, however, that some functions specify the default flags value for when it is specified as 0 (which is what was done in 2.2 for clCreateBuffer) to include more than just CL_MEM_READ_WRITE, e.g. clCreatePipe, for which we have:

flags is a bit-field that is used to specify allocation and usage information such as the memory arena that should be used to allocate the pipe object and how it will be used. The Memory Flags table describes the possible values for flags. Only CL_MEM_READ_WRITE and CL_MEM_HOST_NO_ACCESS can be specified when creating a pipe object. If the value specified for flags is 0, the default is used which is CL_MEM_READ_WRITE | CL_MEM_HOST_NO_ACCESS.

So, the behaviour for CL_MEM_HOST_* group for flags == 0 can't be described just by the comments in memory flags table. I think the correct general (for all functions) interpretation here is that, first, when flags == 0, it is replaced with the specified default (if any) per description of the parameter of particular function, and then, the further points in the memory flags table apply for this adjusted flags value. In particular, if the adjusted flags still doesn't have any flags from CL_MEM_* group, CL_MEM_READ_WRITE is assumed as default behaviour due to comments in the table, and if it doesn't have anything from CL_MEM_HOST_* group, read-write access from host is allowed. This also means that remarks like "If the value specified for flags is 0, the default is used which is CL_MEM_READ_WRITE" are redundant where they appear (because the table takes care of defaulting CL_MEM_READ_WRITE behaviour), so it makes sense why it was removed from 3.0 specification for clCreateBuffer, but it still exists in other places (e.g. see clCreateImage2D here).

YurkoFlisk avatar May 08 '25 12:05 YurkoFlisk