Xamarin Multiple HTTP Requests slow down the app

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?

Jon Skeet
people
quotationmark

There are two many problems with the current code:

  • It's inefficient in terms of reading the data
  • It's synchronously writing the data, which is obviously a problem if you're on the UI thread

The "write the data asynchronously" is probably a simpler fix, so let's look at the inefficiency of reading the data:

  • You're allocating 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 loop
  • You're calling ToList 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!
    

people

See more on this question at Stackoverflow