Boost logo

Boost :

Subject: Re: [boost] Synchronization (RE: [compute] review)
From: Thomas M (firespot71_at_[hidden])
Date: 2014-12-30 10:33:08


On 30/12/2014 15:01, Gruenke,Matt wrote:
> Upon further consideration...
>
> -----Original Message-----
> From: Gruenke,Matt
> Sent: Tuesday, December 30, 2014 8:49
> To: boost_at_[hidden]
> Subject: RE: [boost] Synchronization (RE: [compute] review)
>
>> -----Original Message-----
>> From: Boost [mailto:boost-bounces_at_[hidden]] On Behalf Of Thomas M
>> Sent: Tuesday, December 30, 2014 7:37
>> To: boost_at_[hidden]
>> Subject: Re: [boost] Synchronization (RE: [compute] review)
>
>>> c) a guard for a command-queue as whole [possibly guards for other
>>> classes as well]
>>
>> Why? Convenience?
>>
>> Unless you're using it as a shorthand for waiting on individual events or wait_lists,
>> there's no need. The event_queue is internally refcounted. When the refcount goes
>> to zero, the destructor will block on all outstanding commands.

To some degree it's convenience, that's correct. I find this both more
explicit and straightforward to code:

command_queue::guarantee cqg(cq);

cq.enqueue_write_buffer_async(...);
transform(..., cqg);
cq.enqueue_read_buffer_async(...);
...

than

wait_list wl;
wait_list::guarantee wlg(wl);
wl.insert(cq.enqueue_write_buffer_async(...));
wl.insert(cq.enqueue_task(kern, wl));
wl.insert(cq.enqueue_read_buffer_async(...));

I don't see anything wrong with a compact form which also emphasizes the
grouped nature of all following commands if that's the situation; also I
don't really know what enqueue_task shall exactly look like, but the
STL-ish similarity is lost though.

>
> I should've listed to myself more carefully. I think this means command_queue-level guarantees aren't necessary or useful, because you've either got:
>
> 1) A local command_queue, with a refcount of 1, in which case it will block upon destruction.
>
> 2) A copy of a nonlocal command_queue, in which case there may be unrelated commands in the queue.

I cannot fully follow this; let's presume the convenience aspect is
appreciated, then one reason to offer these guards is to make sure that
other objects, e.g. memory objects, still persist while an asynchronous
operation might use them:

std::vector<float> host_data(...);
cq.enqueue_write_buffer_async(..., &host_data.front());
// here run some algorithm(s)
cq.enqueue_read_buffer_async(..., &host_data.front());

If the command_queue is local then it must become constructed after
host_data, as otherwise at the time of the queue's destruction, i.e.
when it waits for the enqueued operations to finish, host_data is
already dead. To me an explicit guard which I can place flexibly into
any block/scope is ways more clearer (and safer) to handle than
declaration ordering of "primary" objects.
As for the second point I don't see how this alters the game. Every
scope must ensure that its relevant operations have completed before
resources required by the operations become released; if that requires
that commands enqueued earlier (outside) must also complete so shall it be.

I am not saying that such a command-queue guarantee is always the right
thing.

Thomas


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk