init
Some checks failed
Docker. / Ubuntu (push) Has been cancelled
User-agent updater. / User-agent (push) Failing after 15s
Lock Threads / lock (push) Failing after 10s
Waiting for answer. / waiting-for-answer (push) Failing after 22s
Needs user action. / needs-user-action (push) Failing after 8s
Can't reproduce. / cant-reproduce (push) Failing after 8s
Close stale issues and PRs / stale (push) Has been cancelled
Some checks failed
Docker. / Ubuntu (push) Has been cancelled
User-agent updater. / User-agent (push) Failing after 15s
Lock Threads / lock (push) Failing after 10s
Waiting for answer. / waiting-for-answer (push) Failing after 22s
Needs user action. / needs-user-action (push) Failing after 8s
Can't reproduce. / cant-reproduce (push) Failing after 8s
Close stale issues and PRs / stale (push) Has been cancelled
This commit is contained in:
122
Telegram/ThirdParty/dispatch/man/dispatch_apply.3
vendored
Normal file
122
Telegram/ThirdParty/dispatch/man/dispatch_apply.3
vendored
Normal file
@@ -0,0 +1,122 @@
|
||||
.\" Copyright (c) 2008-2017 Apple Inc. All rights reserved.
|
||||
.Dd May 1, 2009
|
||||
.Dt dispatch_apply 3
|
||||
.Os Darwin
|
||||
.Sh NAME
|
||||
.Nm dispatch_apply
|
||||
.Nd schedule blocks for iterative execution
|
||||
.Sh SYNOPSIS
|
||||
.Fd #include <dispatch/dispatch.h>
|
||||
.Ft void
|
||||
.Fo dispatch_apply
|
||||
.Fa "size_t iterations" "dispatch_queue_t queue" "void (^block)(size_t)"
|
||||
.Fc
|
||||
.Ft void
|
||||
.Fo dispatch_apply_f
|
||||
.Fa "size_t iterations" "dispatch_queue_t queue" "void *context" "void (*function)(void *, size_t)"
|
||||
.Fc
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Fn dispatch_apply
|
||||
function provides data-level concurrency through a "for (;;)" loop like primitive:
|
||||
.Bd -literal
|
||||
size_t iterations = 10;
|
||||
|
||||
// 'idx' is zero indexed, just like:
|
||||
// for (idx = 0; idx < iterations; idx++)
|
||||
|
||||
dispatch_apply(iterations, DISPATCH_APPLY_AUTO, ^(size_t idx) {
|
||||
printf("%zu\\n", idx);
|
||||
});
|
||||
.Ed
|
||||
.Pp
|
||||
Although any queue can be used, it is strongly recommended to use
|
||||
.Vt DISPATCH_APPLY_AUTO
|
||||
as the
|
||||
.Vt queue
|
||||
argument to both
|
||||
.Fn dispatch_apply
|
||||
and
|
||||
.Fn dispatch_apply_f ,
|
||||
as shown in the example above, since this allows the system to automatically use worker threads
|
||||
that match the configuration of the current thread as closely as possible.
|
||||
No assumptions should be made about which global concurrent queue will be used.
|
||||
.Pp
|
||||
Like a "for (;;)" loop, the
|
||||
.Fn dispatch_apply
|
||||
function is synchronous.
|
||||
If asynchronous behavior is desired, wrap the call to
|
||||
.Fn dispatch_apply
|
||||
with a call to
|
||||
.Fn dispatch_async
|
||||
against another queue.
|
||||
.Pp
|
||||
Sometimes, when the block passed to
|
||||
.Fn dispatch_apply
|
||||
is simple, the use of striding can tune performance.
|
||||
Calculating the optimal stride is best left to experimentation.
|
||||
Start with a stride of one and work upwards until the desired performance is
|
||||
achieved (perhaps using a power of two search):
|
||||
.Bd -literal
|
||||
#define STRIDE 3
|
||||
|
||||
dispatch_apply(count / STRIDE, DISPATCH_APPLY_AUTO, ^(size_t idx) {
|
||||
size_t j = idx * STRIDE;
|
||||
size_t j_stop = j + STRIDE;
|
||||
do {
|
||||
printf("%zu\\n", j++);
|
||||
} while (j < j_stop);
|
||||
});
|
||||
|
||||
size_t i;
|
||||
for (i = count - (count % STRIDE); i < count; i++) {
|
||||
printf("%zu\\n", i);
|
||||
}
|
||||
.Ed
|
||||
.Sh IMPLIED REFERENCES
|
||||
Synchronous functions within the dispatch framework hold an implied reference
|
||||
on the target queue. In other words, the synchronous function borrows the
|
||||
reference of the calling function (this is valid because the calling function
|
||||
is blocked waiting for the result of the synchronous function, and therefore
|
||||
cannot modify the reference count of the target queue until after the
|
||||
synchronous function has returned).
|
||||
.Pp
|
||||
This is in contrast to asynchronous functions which must retain both the block
|
||||
and target queue for the duration of the asynchronous operation (as the calling
|
||||
function may immediately release its interest in these objects).
|
||||
.Sh FUNDAMENTALS
|
||||
.Fn dispatch_apply
|
||||
and
|
||||
.Fn dispatch_apply_f
|
||||
attempt to quickly create enough worker threads to efficiently iterate work in parallel.
|
||||
By contrast, a loop that passes work items individually to
|
||||
.Fn dispatch_async
|
||||
or
|
||||
.Fn dispatch_async_f
|
||||
will incur more overhead and does not express the desired parallel execution semantics to
|
||||
the system, so may not create an optimal number of worker threads for a parallel workload.
|
||||
For this reason, prefer to use
|
||||
.Fn dispatch_apply
|
||||
or
|
||||
.Fn dispatch_apply_f
|
||||
when parallel execution is important.
|
||||
.Pp
|
||||
The
|
||||
.Fn dispatch_apply
|
||||
function is a wrapper around
|
||||
.Fn dispatch_apply_f .
|
||||
.Sh CAVEATS
|
||||
Unlike
|
||||
.Fn dispatch_async ,
|
||||
a block submitted to
|
||||
.Fn dispatch_apply
|
||||
is expected to be either independent or dependent
|
||||
.Em only
|
||||
on work already performed in lower-indexed invocations of the block. If
|
||||
the block's index dependency is non-linear, it is recommended to
|
||||
use a for-loop around invocations of
|
||||
.Fn dispatch_async .
|
||||
.Sh SEE ALSO
|
||||
.Xr dispatch 3 ,
|
||||
.Xr dispatch_async 3 ,
|
||||
.Xr dispatch_queue_create 3
|
||||
Reference in New Issue
Block a user