I am trying to measure time taken per GET request when downloading a file using AFNetworking. I am downloading a file repeatedly in a loop. The problem I am having is that the way I am measuring total time, it gives a much larger total time than it actually is. For example, for 50 downloads it gives 72 sec but in reality it only took around 5 sec. I also suspect 5 sec is too low for 50 downloads(the download size is 581 kb per file).
How do I effectively measure time in this case? I need time from the moment request is fired till response in received.
My method to download file:
- (void) HTTPGetRequest
{
startTime = CACurrentMediaTime(); // Start measuring time
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:http://myServer];
NSMutableURLRequest *request = [httpClient requestWithMethod:@"GET"
path:@"/download/Text581KB.txt"
parameters:nil];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[httpClient registerHTTPOperationClass:[AFHTTPRequestOperation class]];
// Save downloaded file
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *path = [[paths objectAtIndex:0] stringByAppendingPathComponent:[NSString stringWithFormat:@"Text581KB.txt"]];
operation.outputStream = [NSOutputStream outputStreamToFileAtPath:path append:NO];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
double elapsedTime = (CACurrentMediaTime() - startTime); // Measuring time
totalTime += elapsedTime; // Measuring total time HERE!
[results setString:[NSString stringWithFormat: @"Current Transaction Time: %f sec\nTotal Time: %f sec", elapsedTime, totalTime]];
[_resultLabel performSelectorOnMainThread:@selector(setText:) withObject:results waitUntilDone:NO];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(@"Error: %@", error);
}];
[operation setDownloadProgressBlock:^(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite) { ((int)totalBytesExpectedToWrite));
totalDownloadSize += totalBytesExpectedToWrite;
[_DataTransferredLabel setText:[NSString stringWithFormat:@"Total Download Size: %@", [self getFileSize:totalDownloadSize/1024]]];
}];
[operation setCacheResponseBlock:^NSCachedURLResponse *(NSURLConnection *connection, NSCachedURLResponse *cachedResponse) {
return nil;
}];
[operationQueue addOperation:operation];
}
I am creating a NSOperationQueue in my viewDidLoad:
operationQueue = [NSOperationQueue new];
[operationQueue setMaxConcurrentOperationCount:1]; // using this as I was suspecting downloads were happening in parallel & thus 50 downloads finishing in a few secs
I am invoking the HTTPGetRequest method as follows:
- (IBAction)startDownload:(UIButton *)sender {
totalCount = [[_countText text] longLongValue]; // get # of times to download
long currentCount = 1;
completedCount = 0;
totalTime = 0;
totalDownloadSize = 0;
while (currentCount <= totalCount)
{
[self HTTPGetRequest];
[results setString:@""];
currentCount++;
}
Use AFHTTPRequestOperationLogger.
Another option is to inject the "fire" date in the operation's userInfo by observing the AFNetworkingOperationDidStartNotification notification.
In terms of calculating cumulative time (not elapsed time), I have just created a subclass of
AFHTTPRequestOperation
that captures the start time. Otherwise, you won't know precisely when it started:(Note I'm using
CFAbsoluteTimeGetCurrent
versusCACurrentMediaTime
; use whatever you want, but just be consistent.)Then in the code that's doing the downloads, you can use this
TimedAFHTTPRequestOperation
instead ofAFHTTPRequestOperation
:That code's completion block can then use the
startTime
property ofTimedAFHTTPRequestOperation
to calculate the time elapsed for the given operation and add it to the total time:That's how you calculate the
elapsedTime
and append them together to calculate thetotalTime
.In terms of how to know when the operations are done, I would
modify
HTTPGetRequest
to return aNSOperation
;have
startDownload
create a completion operation and then add all of these operations as dependencies:That achieves several goals, namely creating a completion operation, calculating the total time (as opposed to the total time elapsed).
By the way, I'd also suggest pulling the creation of
AFHTTPClient
out of yourHTTPGetRequest
. You should probably only create one per app. This is especially important in case you ever started usingenqueueHTTPRequestOperation
instead of creating your own operation queue. I also see no need for your call toregisterHTTPOperationClass
.You are incrementing the
totalElapsed
byelapsedTime
, butelapsedTime
is calculated fromstartTime
, which itself represents the time that the jobs were first queued, not when the download actually started. Remember thatHTTPGetRequest
returns almost immediately (having setelapsedTime
). Thus if you're queueing five downloads, I wouldn't be surprised thatHTTPGetRequest
runs five times (and sets and resetsstartTime
five times) before the first request even is initiated.The question is further complicated by the question of whether you're doing concurrent downloads, and if so, what you then mean by "total elapsed". Let's say you have two concurrent downloads, one that takes 5 seconds, another takes 7 seconds. Do you want the answer to be 7 (because they both finished in 7 seconds)? Or do you want the answer to be 12 (because they both finished in a cumulative 12 seconds)?
I'm presuming that you're looking for, in this scenario, 7 seconds, then you should set the
startTime
once before you initiate all of your requests, and then only calculate when all of the downloads are done. You could, for example, rather than doing any calculations inHTTPGetRequest
at all, just add your own operation that is dependent upon all the other operations you added, which calculates the total elapsed. at the very end. Or, if you want the the total elapsed to just reflect the total elapsed while you're in the process of downloading, then just settotalElapsed
rather than incrementing it.