I'm using Xamarin to develop an iOS app. One of the features is to download videos in hls format which means that each video can have between 100 to 1000 chunks to download. So I will need to do between 100 to 1000 http requests. This is working but the app is very slow while downloading, I did a performance test with "Instruments" and the CPU is at 80% and once is finished it goes back to 0%-1%. This is testing on the ipad, not on the simulator.
public async Task CreateDownloadTask (string path){
var response = await _httpClient.GetAsync (GetUrl(path),
HttpCompletionOption.ResponseHeadersRead);
if (!response.IsSuccessStatusCode)
{
Debug.WriteLine ("Error: " + path);
RaiseFault(new RpcFault("Error: Unable to download video", -1));
}
else
{
var totalBytes = response.Content.Headers.ContentLength.HasValue
? response.Content.Headers.ContentLength.Value : -1L;
using (var stream = await response.Content.ReadAsStreamAsync ())
{
var isMoreToRead = true;
var data = new byte[totalBytes];
do
{
_cancelSource.Token.ThrowIfCancellationRequested ();
var buffer = new byte[4096];
int read = stream.ReadAsync(buffer,
0,
buffer.Length,
_cancelSource.Token).Result;
if (read == 0)
isMoreToRead = false;
else {
buffer.ToList ().CopyTo (0, data, receivedBytes, read);
receivedBytes += read;
HlsVideo.TotalReceived += read;
}
} while(isMoreToRead);
var fullPath = GetFullFilePath (path);
_fileStore.WriteFile (fullPath, data);
}
}
What can I do to increase the performance while doing multiple http requests?
There are two many problems with the current code:
The "write the data asynchronously" is probably a simpler fix, so let's look at the inefficiency of reading the data:
buffer
in each iteration. We don't care about the data from the previous iteration, so you can just allocate it once before the while
loopToList
on buffer
, which is going to make a copy - which you're then copying into data
You don't actually need buffer
at all! Instead of two allocations and two copies on each iteration, you can just read the bytes directly into data
:
var data = new byte[totalBytes];
do {
_cancelSource.Token.ThrowIfCancellationRequested ();
int read = stream.ReadAsync(data, receivedBytes, data.Length - receivedBytes,
_cancelSource.Token).Result;
receivedBytes += read;
if (read == 0 || receivedBytes == data.Length)
isMoreToRead = false;
}
HlsVideo.TotalReceived += read;
} while(isMoreToRead);
// TODO: check that receivedBytes == data.Length!
See more on this question at Stackoverflow