Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Process execution time and performance using various dispatch queues and NSOperationQueue

I have a XML in my App bundle. I am parsing this XML file. I parsed this XML using NSXMLParser and using following ways:

  1. running the whole code serially on main thread
  2. Using dispatch queues (GCD):

    2.1 Creating my own dispatch queue using dispatch_queue_create

    2.2 using global queue with High Priority dispatch_get_global_queue

    2.3 using global queue with Low Priority

    2.4 using global queue with Background Priority

  3. using NSOperationQueue

I checked the performance and total time taken in execution of parsing the XML file and found really weird (or may be correct) results.

Following are the codes for above mentioned Parsing ways:

  1. Serial execution on main thread - Execution time 34 msec.

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

2.1 Using dispatch_queue_create - Execution time 68 msec.

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);

2.2 using global queue with High Priority dispatch_get_global_queue - Execution time - 74 msec

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);

2.3 Similarly as 2.2 Using global queue with LOW Priority DISPATCH_QUEUE_PRIORITY_LOW - Execution time - 72 msec

2.4 Similarly as 2.2 Using global queue with BACKGROUND Priority DISPATCH_QUEUE_PRIORITY_BACKGROUND - Execution time - 37 msec

2.5. Using NSOperationQueue (subclassing the NSOperation) - Execution time - 36 msec

Can anyone help me figure out these execution times and why are they so strange (or am I missing something). Why I am getting the best performance with method 1. Why is DISPATCH_QUEUE_PRIORITY_BACKGROUND giving better performance than HIGH. Why is NSOperationQueue giving better results than GCD?

like image 812
Evol Gate Avatar asked Sep 02 '26 15:09

Evol Gate


1 Answers

Update:

I tested the various dispatch and operation queues for for two scenarios:

  1. Invoke a single job that repeatedly parse a large (725kb) XML file 100 times in a single operation; and

  2. Queue 100 operations to parse the same very large XML file one time each.

My results (measured in seconds) on my iPhone 5 were as follows:

  1. For first scenario:

    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. For my second scenario:

    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
    

So I draw two fairly unsurprising conclusions from this:

  • For a computationally intensive background operation at the very least, there did not appear to be much variation between the various concurrent background techniques;

  • When breaking a task into multiple operations, the concurrent background operations (NSOperationQueue, GCD global queues) enjoyed a performance advantage over the serial operations.

I'm not suggesting, though, that a device under resource contention might not exhibit different behavior in terms of scheduling (notably GCD global queue types, see dispatch_queue_priority_t, would affect the scheduling of operations with other concurrent operations queued on the device). I'm only trying to demonstrate empirically that the various queues are not significantly more or less efficient than each other. Having said that, I personally wouldn't use DISPATCH_QUEUE_PRIORITY_HIGH in my apps as I assume that would have the possibility of affecting core iOS functionality.

Also, in the interest of full disclosure, I should admit that I'm focusing on material performance differences. It is quite possible that one mechanism or another might offer performance differences measured in msec. When I'm considering different approaches for background performance, I'm definitely more focused on user-observable performance differences.


Original Answer:

I invoked my parseManyTimes (which repeatedly parses a large XML file) from the main queue:

[self parseManyTimes:@"Main queue"];

Via NSOperation:

NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperationWithBlock:^{
     [self parseManyTimes:@"NSOperation"];
}];
queue = nil;

Via global queues on GCD:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [self parseManyTimes:@"DISPATCH_QUEUE_PRIORITY_DEFAULT"];
});

and

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
    [self parseManyTimes:@"DISPATCH_QUEUE_PRIORITY_HIGH"];
});

and

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
    [self parseManyTimes:@"DISPATCH_QUEUE_PRIORITY_LOW"];
});

and via a GCD serial queue:

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

and the results did not differ significantly from each other (all between 32.2 and 32.5 seconds each). My parsing routine is:

- (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];
}

I ran this on an iPad on a 725kb XML file. Clearly, the way I've structured this, with a very time consuming process, it will tend to was out any negligible variance in how the various queues are dispatched, but rather focus on the background operation. By it doesn't raise any concerns (for me at least) in material performance issues in the various techniques.

The results were:

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
like image 200
Rob Avatar answered Sep 05 '26 06:09

Rob



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!