I recently tried running a .NET application on 64bit versions of Windows and was surprised to notice that all my HttpWebRequest.GetResponse()
calls to web services on my local network were talking huge (around 500ms) time to complete. Here's a few info regarding my test setup:
- .NET 3.5
- Windows Vista Home Premium 32bit, Windows Vista Business 64bit and Windows Server 2008 64bit.
The test code is a slightly modified version of the example described in MSDN's article on HttpWebRequest.GetResponse
. The only modifications I performed were just a loop so that I could time 10 consequtive calls, and Basic Authentication, since the web service I was targeting needed authentication:
using System;
using System.Diagnostics;
using System.Net;
using System.Text;
using System.IO;
public class Program
{
// Specify the URL to receive the request.
public static void Main (string[] args)
{
CredentialCache crCache = null;
Stopwatch s = new Stopwatch();
for (int i = 0; i < 10; i++)
{
s.Reset();
s.Start();
HttpWebRequest request =
(HttpWebRequest)WebRequest.Create (args[0]);
// Set some reasonable limits on resources used by this request
request.MaximumAutomaticRedirections = 4;
request.MaximumResponseHeadersLength = 4;
// Set credentials to use for this request.
if (crCache == null)
{
crCache = new CredentialCache();
crCache.Add(new Uri(args[0]), "Basic",
new NetworkCredential("user", "password"));
}
request.Credentials = crCache;
request.PreAuthenticate = true;
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
Console.WriteLine("Content length is {0}", response.ContentLength);
Console.WriteLine("Content type is {0}", response.ContentType);
// Get the stream associated with the response.
Stream receiveStream = response.GetResponseStream();
// Pipes the stream to a higher level stream reader with the required encoding format.
StreamReader readStream = new StreamReader(receiveStream, Encoding.UTF8);
Console.WriteLine("Response stream received.");
//Console.WriteLine (readStream.ReadToEnd ());
response.Close();
readStream.Close();
s.Stop();
Console.WriteLine("Request took: " + s.ElapsedMilliseconds);
}
}
}
I compiled the program targeting x86 for Windows Vista Home Premium and x64 for the 64bit Windows machines. The three machines were connected on the same network switch together with the machine hosting the web service. Here are the results I got:
- Windows Vista Home Premium 32bit, x86 assembly: The first call to
GetResponse()
was completed in around 150ms, while all consecutive calls took around 10ms. - Windows Vista Business 64bit, Server 2008 64 bit, x86 and x64 assemblies: The first call took around 1000ms, while each consecutive call completed in 500ms. If I disable
HttpWebRequest.PreAuthenticate
, eachGetResponse
completes in 1000ms (which is quite reasonable since in this case each request triggers two separate HTTP requests, one ending up with Unauthorized and one the gets the proper response).
Does anybody have a clue of the reason I get such long GetResponse
delays on the 64 bit versions of Windows?
Edit
Additional information for the problem:
- I measured the response times (via firebug as suggested) when the request is performed with Firefox 3.5 and the request-response delay is identical for all machines, i.e. the problem is not reproduced.
- I did further packet analysis with Wireshark and came up with the following results:
32bit: The tcp conversation is as follows:
- host->server New web request HTTP GET/HTTP/1.1
- server->host Two TCP segments (~5ms delta)
- host->server Tcp Ack (acknowledges both segments) (~10us delta)
- server->host HTTP/1.1 200 OK (~800us delta)
- host->server New web request HTTP GET/HTTP/1.1 & piggybacked TCP ack for previous segment (HTTP/1.1 200 OK) (~10ms delta)
64 bit: The tcp conversation is as follows:
- host->server New web request HTTP GET/HTTP/1.1
- server->host Two TCP segments (~5ms delta, same as 32bit)
- host->server Tcp Ack (acknowledges both segments) (~10us delta, same as 32 bit)
- server->host HTTP/1.1 200 OK (~800us delta, same as 32 bit)
- host->server TCP ack for previous frame (HTTP/1.1 200 OK) (!!! 96ms)
- host->server New web request HTTP GET/HTTP/1.1 (!!! 309ms)
The 500ms I get on the 64 bit machines is mostly occured in the last two steps. Note that this is definitely not related to the TCP stack (since everything works ok with firefox). The reason we get different TCP Ack pattern in the last two steps (piggybacked in 32 bit, while separate TCP Ack frame in 64 bit) is that the new Web Request is delayed for 309+96ms in the 64bit case (so the TCP stack outputs a separate Ack frame, it can not wait for the application layer).
So, it seems like:
- The problem is caused by the delta time between the completion of a web request and the issuing of a new one.
- The problem has something to do with the .NET Framework? (Definitely not TCP related).
- The problems occurs on stock Microsoft code taken from MSDN (MS can't be wrong right?).
Any clues?