使用各种调度队列和NSOperationQueue处理执行时间和性能

Evo*_*ate 3 cocoa objective-c ios

我的App包中有一个XML.我正在解析这个XML文件.我NSXMLParser使用以下方法解析此XML :

  1. 在主线程上串行运行整个代码
  2. 使用调度队列(GCD):

    2.1使用dispatch_queue_create创建自己的调度队列

    2.2使用具有高优先级的全局队列dispatch_get_global_queue

    2.3使用低优先级的全局队列

    2.4使用具有后台优先级的全局队列

  3. 运用 NSOperationQueue

我检查了执行解析XML文件所花费的性能和总时间,发现了非常奇怪(或可能是正确的)结果.

以下是上述解析方法的代码:

  1. 主线程上的串行执行 - 执行时间34毫秒.

    BOOL success = [conf parseXMLFile:[[NSBundle mainBundle] pathForResource:@"Configuration" ofType:@"xml"] didFailWithError:&error];
    if (success) {
        DLog(@"Parsing Complete");
    }
    else
        DLog(@"Parse error %@",[error description]);
    
    Run Code Online (Sandbox Code Playgroud)

2.1使用dispatch_queue_create - 执行时间68毫秒.

dispatch_queue_t backgroundQueue = dispatch_queue_create("BackQueue", NULL);

dispatch_async(backgroundQueue, ^{
    NSError *error = nil;
    BOOL success = [conf parseXMLFile:[[NSBundle mainBundle] pathForResource:@"Configuration" ofType:@"xml"] didFailWithError:&error];
    if (success) {
        DLog(@"Parsing Complete");
    }
    else
        DLog(@"Parse error %@",[error description]);
});

dispatch_release(backgroundQueue);
Run Code Online (Sandbox Code Playgroud)

2.2使用具有高优先级的全局队列dispatch_get_global_queue - 执行时间 - 74毫秒

dispatch_queue_t backgroundQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);

dispatch_async(backgroundQueue, ^{
    NSError *error = nil;
    BOOL success = [conf parseXMLFile:[[NSBundle mainBundle] pathForResource:@"Configuration" ofType:@"xml"] didFailWithError:&error];
    if (success) {
        DLog(@"Parsing Complete");
    }
    else
        DLog(@"Parse error %@",[error description]);
});

dispatch_release(backgroundQueue);
Run Code Online (Sandbox Code Playgroud)

2.3类似于2.2使用低优先级的全局队列DISPATCH_QUEUE_PRIORITY_LOW - 执行时间 - 72毫秒

2.4类似于2.2使用具有后台优先级的全局队列DISPATCH_QUEUE_PRIORITY_BACKGROUND - 执行时间 - 37毫秒

2.5.使用NSOperationQueue(继承NSOperation) - 执行时间 - 36毫秒

任何人都可以帮我弄清楚这些执行时间,为什么它们如此奇怪(或者我错过了什么).为什么我使用方法1获得最佳性能.为什么DISPATCH_QUEUE_PRIORITY_BACKGROUND提供比HIGH更好的性能.为什么NSOperationQueue比GCD提供更好的结果?

Rob*_*Rob 6

更新:

我测试了两种方案的各种调度和操作队列:

  1. 调用单个作业,在单个操作中重复解析大型(725kb)XML文件100次; 和

  2. 队列100操作分别解析同一个非常大的XML文件.

我在iPhone 5上的结果(以秒为单位)如下:

  1. 对于第一种情况:

    Main queue: 18.7
    NSOperation: 18.4
    Global high-priority queue: 18.3
    Global default-priority queue: 18.4
    Global low-priority queue: 18.4
    Global background queue: 18.5
    Serial dispatch queue: 18.3
    
  2. 对于我的第二个场景:

    Main queue: 18.7
    NSOperation: 10.9
    Global high-priority queue: 10.9
    Global default-priority queue: 10.8
    Global low-priority queue: 10.8
    Global background queue: 11.0
    Serial dispatch queue: 18.5
    

因此,我从中得出两个相当不足为奇的结论:

  • 至少对于计算密集型背景操作,各种并发背景技术之间似乎没有太大差异;

  • 将任务分解为多个操作时,并发后台操作(NSOperationQueueGCD全局队列)比串行操作具有性能优势.

但是,我并不是建议资源争用下的设备在调度方面可能不会表现出不同的行为(特别是GCD全局队列类型,请参阅dispatch_queue_priority_t,会影响在设备上排队的其他并发操作的操作调度).我只是想通过经验证明各种队列的效率并没有明显高于或低于其他队列.话虽如此,我个人不会DISPATCH_QUEUE_PRIORITY_HIGH在我的应用程序中使用,因为我认为这可能会影响核心iOS功能.

此外,为了充分披露,我应该承认我专注于材料性能差异.很可能一种机制或另一种机制可能提供以毫秒为单位测量的性能差异.当我考虑不同的背景性能方法时,我肯定更关注用户可观察到的性能差异.


原答案:

parseManyTimes从主队列调用my (它反复解析一个大的XML文件):

[self parseManyTimes:@"Main queue"];
Run Code Online (Sandbox Code Playgroud)

通过NSOperation:

NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperationWithBlock:^{
     [self parseManyTimes:@"NSOperation"];
}];
queue = nil;
Run Code Online (Sandbox Code Playgroud)

通过GCD上的全局队列:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [self parseManyTimes:@"DISPATCH_QUEUE_PRIORITY_DEFAULT"];
});
Run Code Online (Sandbox Code Playgroud)

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
    [self parseManyTimes:@"DISPATCH_QUEUE_PRIORITY_HIGH"];
});
Run Code Online (Sandbox Code Playgroud)

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
    [self parseManyTimes:@"DISPATCH_QUEUE_PRIORITY_LOW"];
});
Run Code Online (Sandbox Code Playgroud)

并通过GCD串行队列:

dispatch_queue_t dispatchQueue = dispatch_queue_create("org.rob.test", NULL);
dispatch_async(dispatchQueue, ^{
    [self parseManyTimes:@"dispatch_queue_create"];
});
Run Code Online (Sandbox Code Playgroud)

并且结果彼此没有显着差异(均在每个32.2和32.5秒之间).我的解析例程是:

- (void)parseManyTimes:(NSString *)type
{
    NSDate *startDate = [NSDate date];
    for (NSInteger i = 0; i < 100; i++)
        [self parse];

    NSLog(@"%@: %.1f", type, [[NSDate date] timeIntervalSinceDate:startDate]);
}

- (void)parse
{
    NSURL *url = [[NSBundle mainBundle] URLForResource:@"personnel" withExtension:@"xml"];
    NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL:url];
    parser.delegate = self;
    [parser parse];
}
Run Code Online (Sandbox Code Playgroud)

我在一台725kb的XML文件上运行这个.显然,我采用非常耗时的过程来构建这种方式,它将倾向于在调度各种队列的方式上出现任何微不足道的差异,而是关注后台操作.对于各种技术中的材料性能问题,它并没有引起任何担忧(至少对我而言).

结果是:

Main queue: 32.3
NSOperation: 32.2
DISPATCH_QUEUE_PRIORITY_DEFAULT: 32.4
DISPATCH_QUEUE_PRIORITY_HIGH: 32.3
DISPATCH_QUEUE_PRIORITY_LOW: 32.5
dispatch_queue_create: 32.3