Very Slow NSURLSession

Hi,


We've been benchmarking NSURLSession against NSURLConnection/dataWithContentsOfURL. The former is consistently slower on the watch/watch SIM:


    NSString *urlString = [NSString stringWithFormat:@"HERE_GOES_THE_SERVER?%d",arc4random_uniform(1000000000)];

    NSLog(@"Started");
    NSData* data = [NSData dataWithContentsOfURL:[NSURL URLWithString: urlString] options:NSDataReadingUncached error:nil];
    NSLog(@"Finished");


    urlString = [NSString stringWithFormat:@"HERE_GOES_THE_SERVER?%d",arc4random_uniform(1000000000)];
    NSURL * url = [NSURL URLWithString: urlString];
    NSLog(@"Started");
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData timeoutInterval:1];
    NSURLSessionDataTask *dataTask = [[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error)
    {
        NSLog(@"Finished");
    }];

    [dataTask resume];


The random number in the URL is to avoid caches, I'm testing against both my own server and some public servers (Google, others).


2015-09-10 16:46:49.321 SpeedTest WatchKit Extension[1153:16410] Started
2015-09-10 16:46:49.474 SpeedTest WatchKit Extension[1153:16410] Finished
2015-09-10 16:46:49.483 SpeedTest WatchKit Extension[1153:16410] Started
2015-09-10 16:46:52.122 SpeedTest WatchKit Extension[1153:16764] Finished


As you can see the first method (dataWithContentsOfURL) is much faster than the second. NSURLConnection was also much faster, so we started using it, but in the GM it has been removed from the watch. So we are back to square one, using NSURLSession, even if it is 20 times slower (much more if we use HTTPS servers).


We've tried all that comes to our mind when trying to configure NSURLSession to no avail, it's always much slower. And now we don't have the alternative (to use a NSURLConnection).


This doesn't happen in iOS9. Any idea of why this is happening? Is it a bug on watchOS?

Replies

We've reported it as bug 22659970.


If watchOS apps' interactions are meassured in seconds (not minutes) NSURLSession should be at least as fast as NSURLConnection (like in iOS). It shouldn't add several seconds to each request.


As a comparison, this is the same code run in the iOS SIM:


2015-09-11 13:43:41.528 SpeedTest[26696:513189] Started
2015-09-11 13:43:41.632 SpeedTest[26696:513189] Finished
2015-09-11 13:43:41.632 SpeedTest[26696:513189] Started
2015-09-11 13:43:41.676 SpeedTest[26696:513344] Finished


So it is something watchOS2 specific.

I've been seeing the same thing, for what it's worth. I thought it was just me, though.

I am seeing this as well. NSURLSession is very slow, regardless of proximity of the paired phone.

Yep. And I see it happening on the simulator as well, so it's not just a hardware/bluetooth/wifi issue.

I'm seeing this on hardware and the simulator.

Have you submited a bug report? The more people submit this the sooner they'll have a look at it.

Have you submited a bug report? The more people submit this the sooner they'll have a look at it.

Unfortuantely I have been seeing the same exact thing. When I previously used NSURLConnection I had no problems, but using NSURLSession I am getting extremley long response times. I did submit a bug report on it as well.

Did you guys try adding a dispatch_async to main thread? This fixed NSURLSession for me. Not quite as fast as the old dataWithContentsOfURL, but much much better:


NSURLSession *session = [NSURLSession sharedSession];
                    [[session dataTaskWithURL:[NSURL URLWithString:_avatarURL]
                            completionHandler:^(NSData *data,
                                                NSURLResponse *response,
                                                NSError *error) {
                                dispatch_async(dispatch_get_main_queue(), ^{
                                    if (data){
                                        _avatarData = data;
                                        [[NSNotificationCenter defaultCenter] postNotificationName:@"AvatarLoaded" object:self];
                                    }
                                    if (error)
                                        NSLog(error.descriptionl);
                                });
                               
                            }] resume];
                }

Doesn't help me....

The delay (~2.5 seconds for me) happens between the dataTask.resume and when the completion block gets called (before dispatch_async).

Seeing the same issue here. Filed a bug with Apple including a sample app demo'ing the issue, so here's hoping they can do something about this.

There’s a lot of backstory here, so much that I can’t possibly comment on it all, but I want to describe at least some of what’s going on.

First up, you can’t test Watch networking performance on the simulator. It’s generally true that the simulator is not a good place to do performance testing, but it’s especially true in this case. The simulator has direct access to the network (you can learn more about this in a thread I recently updated) while the Watch does not. This obviously has a radical effect on performance.

Second, with regards NSURLSession on the Watch, it can work in two different modes:

  • In most cases it will ask the iPhone to do the networking on its behalf.

  • In some cases it can access the network directly.

These modes have different performance characteristics (alas, I don’t have enough experience with the Watch to describe those for you). Regardless, it’s important that you test like with like.

Next, with regards NSURLConnection, it’s not been arbitrarily removed. Rather, it would not work on the Watch because it does all of its networking ‘in process’, and that doesn’t work on the Watch (again, I’m going to point you to the thread I referenced earlier). NSURLSession is capable of doing its networking out of process, which is how it implements both of the modes of operation I described above (the mechanism is much like NSURLSession background sessions on iOS).

Finally, keep in mind that you can use the Watch Connectivity framework to move work from the Watch to the iPhone. That can make a lot of sense for some apps. For example, if the data needs to be post-processed after the download, it’d make sense for that post processing to be done on the iPhone, which then sends an optimised form of the data to the Watch via Watch Connectivity.

If you have specific questions about networking on watchOS, please post them over in Core OS > Networking. After all, watchOS 2 is no longer pre-release (-:

Share and Enjoy

Quinn "The Eskimo!"
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"