Informally speed testing SharePoint Online

28 Mar 2015

SPTransferSpeed is available as a download from Github.

In order to systematically and objectively identify potential bottlenecks between a computer and SharePoint Online, I've modified the console application for benchmarking Azure blob storage to work with SharePoint document libraries. By providing the application with a path to a document library and a file size, it'll report back on the upload and download speeds.

The idea is then to benchmark on an ad hoc basis or periodically through a script. Wrapped in a script, end-users may even run the benchmark and it could store the report in a file on their computer.

Setup

Let's start with common code for initializing the SharePoint context, a utility method for timing code blocks, and general skeleton code:

// 1. Reference Microsoft.SharePoint.Client.dll and Microsoft.SharePoint.Client.Runtime.dll
//    from C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\16\ISAPI or
//    download from https://github.com/OfficeDev/PnP/tree/master/Assemblies/16
// 2. Build as 64 bit assembly to avoid OutOfMemoryExceptions

using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Security;
using System.Threading;
using Microsoft.SharePoint.Client;
using SPOFile = Microsoft.SharePoint.Client.File;

namespace SharePointOnlineSpeedTest {
    class Program {       
        const int Megabyte = 1024 * 1024;
        const int Megabit = 1000 * 1000;

        static ClientContext SetupContext(Uri siteCollection) {
            var user = "rh@bugfree.onmicrosoft.com";
            var password = "password";
            var securePassword = new SecureString();
            password.ToCharArray().ToList().ForEach(securePassword.AppendChar);
            var credentials = new SharePointOnlineCredentials(user, securePassword);
            return new ClientContext(siteCollection) { Credentials = credentials };
        }

        static double Time(Action code) {
            var sw = new Stopwatch();
            sw.Start();
            code();
            sw.Stop();
            return sw.ElapsedMilliseconds / 1000.0;
        }

        static void Main(string[] args) {
            // code below goes here
        }
    }   
}

The SharePoint client side object model offers (at least) two ways of uploading files to a document library. One way is using the FileCreationInformation class:

// take 1
using (var ctx = SetupContext(new Uri("https://bugfree.sharepoint.com/sites/test"))) {
    var library = ctx.Web.Lists.GetByTitle("speedtest");
    var newFile =
        library.RootFolder.Files.Add(
            new FileCreationInformation {
                Url = "randombits",
                Content = System.IO.File.ReadAllBytes(@"c:\users\rh\desktop\randombits")
            });
    ctx.Load(newFile);
    ctx.ExecuteQuery();
}

This approach works well for file sizes less than two megabytes, but otherwise throws a request message too long exception. Hence we turn to the second option, using the SaveBinaryDirect method and the WebDAV protocol.

// take 2
var destination = new Uri(args[0]);
var sizeInMegabytes = int.Parse(args[1]);
var contentLength = sizeInMegabytes * Megabyte;
var randomizedContent = new byte[contentLength];
new Random().NextBytes(randomizedContent);

using (var ctx = SetupContext(destination)) {
    // internally the context uses HttpWebRequest to upload and download files.
    // Thus, when uploading or downloading large files, we may experience
    //   Unhandled Exception: System.Net.WebException: The operation has timed out
    // unless we change the default timeout period of 180 seconds.
    ctx.RequestTimeout = Timeout.Infinite;

    var uploadTime = Time(() => {
        using (var ms = new MemoryStream(randomizedContent)) {
            SPOFile.SaveBinaryDirect(ctx, destination.LocalPath, ms, true);
        }
    });

    var downloadTime = Time(() => {
        var fileInformation = SPOFile.OpenBinaryDirect(ctx, destination.LocalPath);
        using (var sr = new StreamReader(fileInformation.Stream)) { 
            sr.ReadToEnd(); 
        }
    });

    Console.WriteLine(
        "{0} MB uploaded in {1:0.0} seconds at {2:0.0} Mbit/s\r\n" +
        "{0} MB downloaded in {3:0.0} seconds at {4:0.0} Mbit/s",
        sizeInMegabytes, uploadTime, contentLength / uploadTime * 8 / Megabit,
        downloadTime, contentLength / downloadTime * 8 / Megabit);
}

Execution

According to software boundaries and limits for SharePoint Online, the file attachment size limit is 250MB and file upload limit is 2GB per file.

Running the application on a Windows 2012 Azure virtual machine, here's an average result which is likely a good indicator of the maximum speed possible:

%> ./SharePointOnlineSpeedTest.exe https://bugfree.sharepoint.com/sites/test/speedtest/testfile 1024
1024 MB uploaded in 280.9 seconds at 30.6 Mbit/s
1024 MB downloaded in 95.6 seconds at 89.8 Mbit/s

Beware that SharePoint document libraries are version-enabled by default. Repeatedly running the test with large files may cause the site collection to run out of space.

Conclusion

Uploading and downloading a document of one gigabyte is surprisingly fast, considering how SharePoint must store the file in an SQL Server content database as opposed to blob storage build to handle large files. In terms of bandwidth, to some extend we could use SharePoint Online document libraries as an alternative to Azure blob storage. But SharePoint not being transactional will likely cause issues down the road.