On the flip side, my measurements have usually shown that using Apple's run loop integrated async networking can be a substantial CPU hog, and also limits transfer rates pretty noticeably, as compared to running synchronous networking on a thread.
I wrote a simple test program to illustrate this difference, comparing two approaches:
1) Using NSURLConnection to download data, appending to NSMutableData in the delegate's connection:didReceiveData:. Caching is turned off.
2) Using libcurl's easy interface in a thread spawned for each connection, notifying back to the main thread on completion (with performSelectorOnMainThread). Curl does not appear to do any content caching.
The test harness runs each method alternately, with only one connection simultaneously active, reading from machine-local Apache server delivering a ~50MB file for each request.
The first few iterations look like this (tested on my 8 core Mac Pro, running 10.5.6):
curl: Loaded 51711600 bytes in 0.182 seconds (271.66MB/sec), user time: 0.219, sys time: 0.135
Cocoa: Loaded 51711600 bytes in 0.380 seconds (129.88MB/sec), user time: 0.301, sys time: 0.261
curl: Loaded 51711600 bytes in 0.168 seconds (292.85MB/sec), user time: 0.038, sys time: 0.128
Cocoa: Loaded 51711600 bytes in 0.219 seconds (225.35MB/sec), user time: 0.159, sys time: 0.243
curl: Loaded 51711600 bytes in 0.172 seconds (287.13MB/sec), user time: 0.038, sys time: 0.126
Cocoa: Loaded 51711600 bytes in 0.220 seconds (223.90MB/sec), user time: 0.163, sys time: 0.246
The numbers show real time, as measured with [NSDate timeIntervalSinceReferenceDate], and user and system time, measured with getrusage.
Note that both approaches take a bit longer on the first connection, due to setup costs. Presumably they employ HTTP keep-alive for subsequent requests, since we're hitting the same server.
Looking at the later numbers, we can see that NSURLConnection is about 20% slower on throughput. Additionally, user time is 4X that of curl, and system time is close to 2X.
NSURLConnection does have the advantage of offering an easier API that is more Cocoa native. Also, for many applications, there's plenty of CPU power to go around, and the speed difference won't matter much. Still, in a performance-sensitive or CPU-constrained environment, synchronous threaded networking can perform pretty well.
I also compiled libcurl for iPhone, and tested there with a much smaller file on a remote server over WiFi:
curl: Loaded 3891153 bytes in 5.026 seconds (0.74MB/sec), user time: 0.200, sys time: 1.080
Cocoa: Loaded 3891153 bytes in 5.105 seconds (0.73MB/sec), user time: 1.260, sys time: 1.220
curl: Loaded 3891153 bytes in 5.269 seconds (0.70MB/sec), user time: 0.180, sys time: 0.770
Cocoa: Loaded 3891153 bytes in 5.714 seconds (0.65MB/sec), user time: 1.440, sys time: 1.300
curl: Loaded 3891153 bytes in 5.274 seconds (0.70MB/sec), user time: 0.170, sys time: 0.770
Cocoa: Loaded 3891153 bytes in 5.031 seconds (0.74MB/sec), user time: 1.140, sys time: 1.180
Here, the throughput is basically the same for both methods, but NSURLConnection consumes a rather startling 6-8X the CPU to get there.
Obviously there are many other interesting comparisons that could be done, including:
- Using lower level CFSocket APIs, rather than high-level NSURLConnection
- More than one simultaneous connection
- Using HTTPS
- Re-using threads for the curl test, rather than creating a new one every time
Still, my early testing indicates that libcurl consumes far fewer CPU cycles than NSURLConnection for this specific scenario, which could be quite useful on resource-starved iPhones and iPods.
Also, both methods are implemented in a fairly naive way, so it would be interesting to hear any feedback on how either could be improved.
Regards,
Ladd