dispatch_apply(3)

πŸ“¦ version 0~svn197, in ubuntu jammy

πŸ’– dispatch_apply(3) πŸ’–

okay bestie, gather round, because today we're talking about dispatch_apply from the Library Functions Manual and honestly?? it's giving parallel-for-loop realness and I am obsessed. ✨


🌸 NAME 🌸

dispatch_apply β€” schedule blocks for iterative execution πŸ¦‹


✨ SYNOPSIS ✨

#include <dispatch/dispatch.h>

void
dispatch_apply(size_t iterations, dispatch_queue_t queue,
    void (^block)(size_t));

void
dispatch_apply_f(size_t iterations, dispatch_queue_t queue,
    void *context, void (*function)(void *, size_t));

πŸŽ€ DESCRIPTION πŸŽ€

So like. dispatch_apply() gives you data-level concurrency through a lil for (;;)-loop-like primitive. it's literally a for loop but make it πŸ’…concurrentπŸ’…. no cap.

dispatch_queue_t the_queue = dispatch_get_concurrent_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT);
size_t iterations = 10;

// 'idx' is zero indexed, just like:
// for (idx = 0; idx < iterations; idx++)

dispatch_apply(iterations, the_queue, ^(size_t idx) {
        printf("%zu\n", idx);
});

okay important thing, bestie, listen up πŸ‘‚ β€” just like a for (;;) loop, dispatch_apply() is synchronous. it does not return until it's done doing its thing. so if you want async behavior?? you gotta wrap that call to dispatch_apply() with a call to dispatch_async() against another queue. that's the move. 🫢


sometimes, when the block you pass to dispatch_apply() is simple, using striding can tune performance. and the way that you calculate the optimal stride?? honestly bestie, that's best left to experimentation πŸ™ˆ. start with a stride of one and work upwards until you get the performance you want (maybe using a power of two search, iykyk):

#define STRIDE  3

dispatch_apply(count / STRIDE, queue, ^(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);
}

πŸ”₯ FUNDAMENTALS πŸ”₯

conceptually, dispatch_apply() is just a convenient wrapper around dispatch_async() and a semaphore to wait for completion. cute, right?? but in practice, the dispatch library optimizes this function, so it's not just doing the obvious thing. slay. πŸ‘‘

and plot twist: dispatch_apply() is itself a wrapper around dispatch_apply_f(). wrappers within wrappers, it's giving layers. 🧚


πŸ’œ SEE ALSO πŸ’œ


okay that's the whole page bestie, you ate that πŸ’…βœ¨ this one came from the Darwin crew, dated May 1, 2009, and it lives in section 3 of the manual. go forth and parallelize responsibly, mwah πŸŽ€πŸ¦‹πŸ’–

πŸ“¦ where this page comes from