Improving performance of multithreaded HttpWebRequests in .NET
Asked Answered
P

6

30

I am trying to measure the throughput of a webservice.

In order to do that, I have written a small tool that continuously sends requests and reads responses from a number of threads.

The contents of the inner loop of each thread looks like this:

public void PerformRequest()
{
  WebRequest webRequest = WebRequest.Create(_uri);

  webRequest.ContentType = "application/ocsp-request";
  webRequest.Method = "POST";
  webRequest.Credentials = _credentials;
  webRequest.ContentLength = _request.Length;
  ((HttpWebRequest)webRequest).KeepAlive = false;

  using (Stream st = webRequest.GetRequestStream())
    st.Write(_request, 0, _request.Length);

  using (HttpWebResponse httpWebResponse = (HttpWebResponse)webRequest.GetResponse())
  using (Stream responseStream = httpWebResponse.GetResponseStream())
  using (BufferedStream bufferedStream = new BufferedStream(responseStream))
  using (BinaryReader reader = new BinaryReader(bufferedStream))
  {
    if (httpWebResponse.StatusCode != HttpStatusCode.OK)
      throw new WebException("Got response status code: " + httpWebResponse.StatusCode);

    byte[] response = reader.ReadBytes((int)httpWebResponse.ContentLength);
    httpWebResponse.Close();
  }      
}

It seems to work okay, except that something seems to be limiting the tool. If I run two instances of the tool with each 40 threads, I get significantly more throughput than one instance with 80 threads.

I found the ServicePointManager.DefaultConnectionLimit property, which I set to 10000 (and it makes no difference if I set it through app.config as suggested by Jader Dias).

Are there any other settings in .NET or on my machine that can influence the performance? (I am running Vista, but I see the same problem on Windows Server 2003).

Perhaps some restrictions on how many connections a single process can make?

Privilege answered 23/12, 2008 at 13:50 Comment(2)
I know this question is from '08, but you may be interested in some of the new parallelism features of .net 4.5Stationary
@user389823 those parallelism features will make it easier to implement the solution, but they will not solve the problem of the DefaultConnectionLimit.Eider
S
43

You must set the maxconnection parameter at the app.config or web.config file:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.net>
    <connectionManagement>
      <add address="*" maxconnection="80"/>
    </connectionManagement>
  </system.net>
</configuration>

Values up to 100 work very well with Windows XP.

Update: I just found out that the method above is an alternative way to set the System.Net.ServicePointManager.DefaultConnectionLimit

Septate answered 12/1, 2009 at 18:32 Comment(3)
That last update seems to be the perfect way to find out wat maxconnections is set to when using autoConfig="true" in processModel. Just adding some keywords so that other people will be able to find this answerIsochromatic
Just adding a note here to mention that this did not work for me (just as ServicePointManager.DefaultConnectionLimit did not).Privilege
ServicePointManager.DefaultConnectionLimit needs to be set before the ServicePoint is created and does not work in MonoBazooka
T
2

it could be the connection limit that has been imposed recently.

http://www.speedguide.net/read_articles.php?id=1497

and

http://www.mydigitallife.info/2007/04/09/windows-vista-tcpipsys-connection-limit-patch-for-event-id-4226/

Timotheus answered 23/12, 2008 at 14:9 Comment(1)
I don't get the entry in the eventlog, so I don't think that is it. It was a good suggestion, though. Thanks!Privilege
T
2

have you tried increasing the max connections in the network settings?

http://msdn.microsoft.com/en-us/library/fb6y0fyc.aspx

Timotheus answered 23/12, 2008 at 16:43 Comment(0)
S
1

Keep in mind multithreaded code can always cause contention on any shared resources and even if you're not explicitly sharing anything you might be using classes that are sharing resources under the covers.

If you are really getting better performance with 2 40 thread exes than 1 80 thread exe then you'll need to start your investigation with shared resources. And if that is the case, the code you cited is far less interesting than the code that creates and manages the threads.

The other thing I'd throw out there is there are several tools that you can get that will do this type of thing for you generically. See http://support.microsoft.com/kb/231282. Also included in Visual Studio (I'm not sure what skus) is a new generation of web application performance testing tools. And I'm sure if you looked you could find some non-MS stuff too.

Spittle answered 23/12, 2008 at 17:17 Comment(0)
N
0

There are two important aspects w.r.t performance:

  1. One aspect is as suggested by all, the number of TCP connections been used by client (generally better if these connections are persisted (keep alive = true)) for detail refer to : http://msdn.microsoft.com/en-us/library/system.net.servicepoint.connectionlimit(v=vs.110).aspx, How and where the TCP connection has been created in httpwebrequest, and how is it related to servicepoint? , Why System.Net.ServicePoint.ConnectionLimit uses '7FFFFFFF' (Int32.MaxValue/2147483647) when a client connects to a service on 'localhost'? , System.Net.ServicePointManager.DefaultConnectionLimit and .MaxServicePointIdleTime )

  2. Second aspect is rather than using multiple new threads/or using worker threads to do work in parallel using synchronous calls (like httpwebrequest.getrequeststream) in the code snippet, embracing async model completely (for ex, begin/endrequeststream, or new task variations). This way CPU will be always busy and let I/O completion port thread simply send the response in a worker (thread pool) thread by invoking callback. (you may refer to: How does .NET make use of IO Threads or IO Completion Ports?, http://blog.marcgravell.com/2009/02/async-without-pain.html, HttpWebRequest and I/O completion ports )

Nelsonnema answered 22/6, 2014 at 4:21 Comment(0)
A
-1

How are you creating your threads? I assume that since you know you have 80 threads, you're not using the threadpool manager, because with the threadpool manager you can ask for as many threads as you like and you'll only get 25 active threads at a time. If you create the threads manually with an array then you'll actually get as many as you need, however they are still in the same process space, so that might limit them over threads running in separate processes.

You might also look into which apartment style the threads are getting created with, I believe the Thread class ctor uses STA by default. Try MTA and see if they affects performance.

Animator answered 23/12, 2008 at 16:51 Comment(3)
Only the initial version (or perhaps the first beta) of the 1.0 framework used STA threads. Keep in mind too that this threading context only has to deal with COM interop and the COM model. Unless they're using a COM object it doesn't matter here.Spittle
You can set the threading model of your entry point with SetApartmentThread or the STAThreadAttribute in .NET 2.0 like this: msdn.microsoft.com/en-us/library/…Animator
Right but by default .Net threads do not have an apartment specified and will specify one when using an object. Further, the thread apartment is still only important if you're either being used as a com object (CCW) or using a com object (RCW).Spittle

© 2022 - 2024 — McMap. All rights reserved.