HttpWebRequest and HttpWebResponse ideal async buffer sizes
Asked Answered
S

2

0

I'm trying to use HttpWebRequest and HttpWebResponse in .NET 3.5, running them in asynchronously: BeginGetRequestStream, EndGetRequestStream, BeginWrite, EndWrite, BeginGetResponse, EndGetResponse, BeginRead, EndRead - all parts of handling a request are asynchronous.

I have a few threads that send a large number of concurrent requests. EndRead and EndWrite are both blocking operations - they block the current thread while the actual read/write against the stream is done, I'm trying to come up with an ideal input/output buffer size for these operations.

My reasoning is this: as I have multiple requests active at a time, they'll keep firing callbacks to let the thread know there's some data available or data was sent. If my buffers are large, reading/writing the data through the wire will take longer, so EndRead/EndWrite will block longer. This would force the other requests on the same thread to wait a bit longer, since their notifications will have to wait until the thread is unblocked.

So, my question is, what would be a good read / write buffer size in this situation. I was thinking 2048 bytes each, but some sample code I saw in various blogs show wildly different values.

Thanks in advance for any ideas.

Shawna answered 5/10, 2011 at 19:0 Comment(0)
T
1

I think a better solution would be not to worry about the buffer sizes too much, but don't block the threads. If you pass a delegate to the callback parameter of the Begin* methods, that callback is executed when the operation completes and you can call End* from there, which will (almost) immediately return. No blocking necessary.

And regarding the buffer sizes, if they really matter to you, you should profile and find out what works best in your specific situation.

Tennies answered 5/10, 2011 at 21:5 Comment(7)
[If you pass a delegate to the callback parameter of the Begin* methods, that callback is executed when the the operation completes and you can call End* from there, which will (almost) immediately return. No blocking necessary]. Funny, who does the real "Begin*" job. It is a thread from ThreadPool that does the call+waiting for you. what is different than creating your own thread that does the same job?Bargain
That depends. But in case of IO operations, IO completion ports are used. That means the Begin method starts the work and sets up the port. When the work is completed, port fires and the callback is executed on a thread pool thread. And it's different from creating new thread for that, because creating new thread always allocates a million bytes.Tennies
So you say,BeginA wait in a delegate fxn, in that fxn start BeginB wait in another delegate etc. (instead of calling A();B() in a thread)Do you really know that his code requires that complexity. As I said in my answer "premature optimization is the root of all evil"Bargain
I didn't say anything about any waiting. And this is not a premature optimization if there is “large number of concurrent requests”, as the OP said.Tennies
I know. Waiting is done by threads created(or already working) with BeginA* functions, and your delegate is called when result is ready. It was just to simplify the workflow. I don't know how much you may find it related to this question but, I love this blog(chaosinmotion.com/blog/?p=622)Bargain
@LB I'm not quite sure why you linker that chaosinmotion page - it looks interesting but I don't see a connection. Using many threads to an I/O-related problem is a bad solution - it uses a lot of system resources while most of the threads do no work. In this case, I'm using HttpWebRequest which uses ThreadPool and it's completion port threads; the waits happen there. My code simply does the stream reading/writing which are blocking operations. The faster they finish, the faster the thread can get new callbacks from other completion port threads.Shawna
@svick: thanks, I think you're right - I forgot that the callback would execute on the completion port thread. You're right, I probably don't have to worry about the buffer sizes all that much.Shawna
N
1

There's no definitive rule as to the actual values you should set, beyond avoiding the obvious extremes. It really depends on the type of data you're transfering, and how much of it there is. You probably want to set your write buffer quite high, but leave your read buffer lower. This is because writes are (usually) more expensive than reads when it comes to this kind of thing.

The best thing to do in this situation is try a few values and see how well they scale. You can always change them later if necessary.

Neau answered 5/10, 2011 at 19:7 Comment(0)
T
1

I think a better solution would be not to worry about the buffer sizes too much, but don't block the threads. If you pass a delegate to the callback parameter of the Begin* methods, that callback is executed when the operation completes and you can call End* from there, which will (almost) immediately return. No blocking necessary.

And regarding the buffer sizes, if they really matter to you, you should profile and find out what works best in your specific situation.

Tennies answered 5/10, 2011 at 21:5 Comment(7)
[If you pass a delegate to the callback parameter of the Begin* methods, that callback is executed when the the operation completes and you can call End* from there, which will (almost) immediately return. No blocking necessary]. Funny, who does the real "Begin*" job. It is a thread from ThreadPool that does the call+waiting for you. what is different than creating your own thread that does the same job?Bargain
That depends. But in case of IO operations, IO completion ports are used. That means the Begin method starts the work and sets up the port. When the work is completed, port fires and the callback is executed on a thread pool thread. And it's different from creating new thread for that, because creating new thread always allocates a million bytes.Tennies
So you say,BeginA wait in a delegate fxn, in that fxn start BeginB wait in another delegate etc. (instead of calling A();B() in a thread)Do you really know that his code requires that complexity. As I said in my answer "premature optimization is the root of all evil"Bargain
I didn't say anything about any waiting. And this is not a premature optimization if there is “large number of concurrent requests”, as the OP said.Tennies
I know. Waiting is done by threads created(or already working) with BeginA* functions, and your delegate is called when result is ready. It was just to simplify the workflow. I don't know how much you may find it related to this question but, I love this blog(chaosinmotion.com/blog/?p=622)Bargain
@LB I'm not quite sure why you linker that chaosinmotion page - it looks interesting but I don't see a connection. Using many threads to an I/O-related problem is a bad solution - it uses a lot of system resources while most of the threads do no work. In this case, I'm using HttpWebRequest which uses ThreadPool and it's completion port threads; the waits happen there. My code simply does the stream reading/writing which are blocking operations. The faster they finish, the faster the thread can get new callbacks from other completion port threads.Shawna
@svick: thanks, I think you're right - I forgot that the callback would execute on the completion port thread. You're right, I probably don't have to worry about the buffer sizes all that much.Shawna

© 2022 - 2024 — McMap. All rights reserved.