Edit
I read through some articles on blocks and fast enumeration and GCD and the like. @Bbum, who's written many articles on the subject of GCD and blocks, says that the block enumeration methods are always as fast or faster than the fast enumeration equivalents. You can read his reasoning here.
While this has been a fascinating, intellectual conversation, I agree with those who said that it really depends on the task at hand.
I have some tasks to accomplish and I need them done fast, cheap, and efficiently. Apple gives us many choices for how we want to enumerate an array, but I'm not sure which to choose.
Fast Enumeration
for (id obj in array)
{
/* Do something with |obj|. */
}
Nonconcurrent Block Enumeration
[array enumerateObjectsUsingBlock: ^(id obj, NSUInteger idx, BOOL *stop) {
/* Do something with |obj|. */
}];
Concurrent Block Enumeration
[array enumerateObjectsWithOptions: NSEnumerationConcurrent usingBlock: ^(id obj, NSUInteger idx, BOOL *stop) {
/* Do something with |obj|. */
}];
GCD Apply
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_apply([array count], queue, ^(size_t idx) {
id obj = [array objectAtIndex: idx];
/* Do something with |obj|. */
});
GCD Async Apply
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_async(queue, ^(void) {
dispatch_apply([array count], queue, ^(size_t idx) {
id obj = [array objectAtIndex: idx];
/* Do something with |obj|. */
});
});
Or perhaps something with NSBlockOperation
s or an NSOperationQueue
?
TIA, Alex.
The fastest code is the code that reaches the market first.
Seriously -- unless you have a measurable performance problem, this particular choice should occupy no more of your time than it takes to answer which of these patterns fits the most naturally with my project's style?
Note: adressing a performance problem by moving from serial to concurrent execution usually results having two problems; performance & concurrency.
It really depends on the task at hand.
Processing more than one iteration at a time requires spawning threads. If the logic in the iterations is parallelizable and takes more time than it would take to spawn a thread, then use threads. Also, if you have so many items in the array that it would take less to spawn a thread than to walk through the whole array, divide your array into a few pieces and process them in parallel.
Otherwise, spawning threads to iterate through the array is overhead. Even if the OS takes care of that for you, it still does need to spawn them. That takes time and resources and context switching at runtime (depending on the number of CPUs available, load, scheduler, etc).
It all comes down to whether spawning a new thread takes longer than walking through the whole array. You can find that out using the profiling tools.
If you watch the WWDC video "Hidden Development Gems in iOS7" (this question is older than that video granted), you will see that simple for loop enumerations can be better implemented using GCD's:
[array enumerateObjectsWithOptions: NSEnumerationConcurrent usingBlock: ^(id obj, NSUInteger idx, BOOL *stop) { /* Do something with |obj|. */ }];
Although it does depend on the task at hand to an extent and shipping software is of the most importance, there are definitely easy ways to keep performance issues from cropping up as a tech debt issue in the future when your application starts to scale. For example, you may have a large network fetch result. From this result, you may have to iterate over the fetched objects and update some model or series of models (not Core Data models as Core Data is not thread safe! Additional measures must be taken to work with Core Data on multiple threads. This is a deep topic you will want to research if you are considering working on Core Data from a multi-threading scenario). Considering you are not performing updates to the UI (as that is not something you would want to do off of the main thread), you can use this method to take full advantage of the hardware of your device. Instead of the for loop using only one core at a time, it will fully utilize the available cores in parallel, therefore increasing performance and potentially almost halving the time of execution. This is not something you will want to do without giving any thought to the array you are enumerating over and the tasks you are performing from within the block, but is useful for a wide array of cases if used carefully.