我有另一个问题,我曾问过有关使用Grand Central Dispatch将代码从顺序处理转换为并行处理的问题。
我将复制问题文本以使事情变得容易...
我有一个必须通过20个测试的NSNumbers数组。如果一项测试失败,则该数组无效;如果所有测试通过,则该数组无效。我正在尝试以这样的方式进行操作:一旦第一次失败发生,它就会停止进行其余的测试。如果第三次测试失败,则停止评估其他测试。
每个单独的测试在失败时都返回YES,在没有问题时返回NO。
我正在尝试将已有的代码(即串行处理)转换为具有中央集中调度的并行处理,但是我无法将其束之高阁。
这就是我所拥有的。
首先定义要进行的测试。该数组用于运行测试。
#define TESTS @[ \
@"averageNotOK:", \
@"numbersOverRange:", \
@"numbersUnderRange:",\
@"numbersForbidden:", \
// ... etc etc
@"numbersNotOnCurve:"]
- (BOOL) numbersPassedAllTests:(NSArray *)numbers {
NSInteger count = [TESTS count];
for (int i=0; i<count; i++) {
NSString *aMethodName = TESTS[i];
SEL selector = NSSelectorFromString(aMethodName);
BOOL failed = NO;
NSMethodSignature *signature = [[self class] instanceMethodSignatureForSelector:selector];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
[invocation setSelector:selector];
[invocation setTarget:self];
[invocation setArgument:&numbers atIndex:2];
[invocation invoke];
[invocation getReturnValue:&failed];
if (failed) {
return NO;
}
}
return YES;
}
这可以完美地工作,但是可以顺序执行测试。
在用户的帮助下处理代码后,我使用中央集中调度程序获得了以下代码:
- (BOOL) numbersPassedAllTests:(NSArray *)numbers {
volatile __block int32_t hasFailed = 0;
NSInteger count = [TESTS count];
__block NSArray *numb = [[NSArray alloc] initWithArray:numbers];
dispatch_apply(
count,
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0),
^(size_t index)
{
// do no computation if somebody else already failed
if(hasFailed) {
return;
}
SEL selector = NSSelectorFromString(TESTS[index]);
BOOL failed = NO;
NSMethodSignature *signature = [[self class] instanceMethodSignatureForSelector:selector];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
[invocation setSelector:selector];
[invocation setTarget:self];
[invocation setArgument:&numb atIndex:2];
[invocation invoke];
[invocation getReturnValue:&failed];
if(failed)
OSAtomicIncrement32(&hasFailed);
});
return !hasFailed;
}
“活动监视器”显示了似乎正在被更密集地使用的核心,但是此代码比顺序运行的旧代码慢至少100倍!
怎么可能?
如果您要调用的方法很简单,那么创建所有这些线程的开销可能会抵消并发所获得的任何优势。正如《并发编程指南》中的“同时执行循环迭代”部分所述:
您应该确保您的任务代码在每次迭代中都进行合理的工作量。与您分派到队列中的任何块或函数一样,安排该代码执行的开销。如果循环的每次迭代仅执行少量工作,则调度代码的开销可能会超过将其分派到队列可能带来的性能收益。如果在测试期间发现这是正确的,则可以使用跨步来增加每次循环迭代期间执行的工作量。跨步时,您可以将原始循环的多个迭代组合到一个块中,并按比例减少迭代计数。例如,如果您最初执行100次迭代,但决定使用4的步幅,则现在从每个块执行4次循环迭代,您的迭代计数为25。循环代码方面的改进。”
与“改进循环代码”的链接通过跨步实现了示例,从而使线程数量与每个线程完成的工作量保持平衡。您需要做一些实验才能找到适合您方法的平衡点,然后尝试不同的跨度值,直到获得最佳性能。
在与CPU绑定的进程的实验中,我发现执行两个线程时获得了巨大的收益,但此后却有所减少。它可能会根据您所调用的方法中的内容而有所不同。
顺便问一下,您要调用的这些方法在做什么?如果您执行任何需要主线程的操作(例如,UI更新),那也会使结果产生偏差。为了进行比较,我建议您以串行示例为例,并将其分发到后台队列(作为单个任务),并查看通过这种方式获得的性能。这样,您就可以区分主队列和后台队列的相关问题,以及我上面讨论的线程过多的开销问题。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句