1

Recently I am testing http "requests per second" difference on Threading and Async/Await.

Using Async/Await is always slighty better(~30%) than Threading (2000-3000 requests per second) until I change the httpwebrequest from "GET" to "POST"

Threading version is still normal (~2000-3000 requests per seconds) with high thread amount like 2000 threads

Async/Await version becomes slow and even stuck under high amount of async task situation( ~500 requests per second, some tasks (~1%) do not finish (unreasonable time like 1 minute) although all other tasks were all finished very early)

Please shed some light on this, I can barely think of a reason why this happens.

Here's the function that stuck

using (Stream postStream = await req.GetRequestStreamAsync())
{
    await postStream.WriteAsync(bytes, 0, bytes.Length); //This is the function that stuck in async version
}

Here's my test code

public static Amount = 0;
public void StartSync(int threadAmount)
{
            Amount = 0;
            Thread[] thread = new Thread[threadAmount];
            Stopwatch sw = new Stopwatch();
            sw.Start();
            for (int i = 0; i < thread.Length; i++)
            {
                thread[i] = new Thread(() =>
                {
                    SyncTest();
                });
                thread[i].IsBackground = true;
                thread[i].Start();
            }
            for (int j = 0; j < thread.Length; j++)
            {
                thread[j].Join();
            }
            sw.Stop();
            MessageBox.Show("Total requests: " + amount + "\r\nTotal seconds: " + (sw.ElapsedMilliseconds / 1000.0) + "\r\nRequests per second: " + Convert.ToDouble(amount) / (sw.ElapsedMilliseconds / 1000.0));
}
public void StartAsync(int threadAmount)
{
            Amount = 0;
            Task[] task = new Task[threadAmount];
            Stopwatch sw = new Stopwatch();
            sw.Start();
            for (int i = 0; i < threadAmount; i++)
            {
                task[i] = AsyncTest();
            }

            await Task.WhenAll(task);
            sw.Stop();
            MessageBox.Show("Total requests: " + amount + "\r\nTotal seconds: " + (sw.ElapsedMilliseconds / 1000.0) + "\r\nRequests per second: " + Convert.ToDouble(amount) / (sw.ElapsedMilliseconds / 1000.0));
}
public async Task<bool> AsyncTest()
{

            HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create("http://www.google-analytics.com/__utm.gif");
            req.Method = "POST";
            req.AllowAutoRedirect = false;
            byte[] bytes = Encoding.ASCII.GetBytes("test");
            req.ContentType = "application/x-www-form-urlencoded";
            req.ContentLength = bytes.Length;


            using (Stream postStream = await req.GetRequestStreamAsync())
            {
                await postStream.WriteAsync(bytes, 0, bytes.Length); //This is the function to be investigated. Adding this line slows down async side significantly
            }

            using (HttpWebResponse res = (HttpWebResponse)await req.GetResponseAsync())
            using (Stream responseStream = res.GetResponseStream())
            {
                StreamReader sr = new StreamReader(responseStream, Encoding.ASCII);
                string resp = await sr.ReadToEndAsync();
            }
            Amount++;
            return true;
}

public bool SyncTest()
{
            HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create("http://www.google-analytics.com/__utm.gif");
            req.Method = "POST";
            req.AllowAutoRedirect = false;
            byte[] bytes = Encoding.ASCII.GetBytes("test");
            req.ContentType = "application/x-www-form-urlencoded";
            req.ContentLength = bytes.Length;
            using (Stream oStreamOut = req.GetRequestStream())
            {
                oStreamOut.Write(bytes, 0, bytes.Length);
            }
            using (HttpWebResponse webResponse = (HttpWebResponse)req.GetResponse())
            {
                using (Stream responseStream = webResponse.GetResponseStream())
                {
                    StreamReader sr = new StreamReader(responseStream, Encoding.ASCII);
                    string resp = sr.ReadToEnd();
                }
            }
            Amount++;
            return true;
}

Update 1:

I tested WriteAsync in memorystream with high amount of async tasks but the slow down did not happen

public async Task<bool> AsyncWrite()
{
    byte[] bytes = Encoding.ASCII.GetBytes("test");
    using (MemoryStream ms = new MemoryStream())
    {
        await ms.WriteAsync(bytes, 0, bytes.Length);
    }
}

Update 2:

I plugged out my router, async version becomes normal, no more stuck. Only with router connected, the weird slow down happens. I cannot explain why this happened to async version only but not threading...

Isolet Chan
  • 417
  • 6
  • 20
  • The answer to this question might help - http://stackoverflow.com/questions/416306/performance-of-httpwebrequest-using-post?rq=1 – Daniel Kelley Sep 04 '14 at 14:42
  • Caching should not be a factor in my situation. I am comparing threading and async/await, both of them have similar performance until switching to "POST" request. I can tell it is the WriteAsync method that stuck after some testing – Isolet Chan Sep 04 '14 at 14:54
  • 2
    Why do you only have a using scope over the webResponse in the sync test and not the async one? – i3arnon Sep 04 '14 at 15:26
  • I was comparing requests performance on delaying the garbage collection and disposing immediately as well (No significant difference). I happened to copy wrong piece of code. All the test is carried out with proper "using". Thank you for your catch, edited the code – Isolet Chan Sep 05 '14 at 00:46
  • Have you considered that since creating threads takes some time, there might be a difference in how *the sever* (Google Analytics) behaves? E.g. getting 1000 requests over 1 ms might be considered a DoS attack, but not 1000 over 2 ms (completely made up numbers)? – svick Sep 11 '14 at 19:59
  • Yes, I always checked the response status code before and after my test. The result only counts when the http status code is still 200. And the real numbers are like 2000 over 1 second in threading and 2500 over 1 second in async/await. Moreover, the stuck only happens with router. Should not be a server problem. Thank you for your help! – Isolet Chan Sep 12 '14 at 00:45

0 Answers0