RejectedSoftware Forums

Sign up

Pages: 1 2

requestHTTP performance

Hello everyone,

I've been having some performance problems in my REST interface and I've tracked it down to the performance of requestHTTP.

I've created a very simple application which demonstrates this problem:

shared static this()
{
    import std.stdio;
    import vibe.http.client;
    import vibe.stream.operations;

    int cnt = 0;
    foreach(i; 0..1_000)
    {
        requestHTTP("an example URL I am working with",
            (scope req) {
                req.method = HTTPMethod.GET;
            },
            (scope res) {
                res.bodyReader.readAll();
                cnt++;
                if (i%50 == 0) i.writeln;
            }
        );
    }
}

This takes about 40 seconds, which is way too much. I am certain that it's not caused by the server, as that can do a few thousands requests per second.

What I've noticed is that the application probably spends a lot of time waiting, because it doesn't use a lot of processing power (it uses about 1% of my CPU). This is expected, as it has to wait for the network (even though I'm running the server on the same machine), but still seems too low.

The situation gets better when I run the request in a task (using runTask), but such an approach requires a lot of memory for all the tasks and is not applicable in my case, where my server has to send a request to another server when it itself receives a request.

Any ideas how to fix this? Thanks.

Re: requestHTTP performance

On Tue, 09 Jun 2015 20:01:41 GMT, Zdeněk wrote:

Hello everyone,

I've been having some performance problems in my REST interface and I've tracked it down to the performance of requestHTTP.

I've created a very simple application which demonstrates this problem:

shared static this()
{
    import std.stdio;
    import vibe.http.client;
    import vibe.stream.operations;

    int cnt = 0;
    foreach(i; 0..1_000)
    {
        requestHTTP("an example URL I am working with",
            (scope req) {
                req.method = HTTPMethod.GET;
            },
            (scope res) {
                res.bodyReader.readAll();
                cnt++;
                if (i%50 == 0) i.writeln;
            }
        );
    }
}

This takes about 40 seconds, which is way too much. I am certain that it's not caused by the server, as that can do a few thousands requests per second.

What I've noticed is that the application probably spends a lot of time waiting, because it doesn't use a lot of processing power (it uses about 1% of my CPU). This is expected, as it has to wait for the network (even though I'm running the server on the same machine), but still seems too low.

The situation gets better when I run the request in a task (using runTask), but such an approach requires a lot of memory for all the tasks and is not applicable in my case, where my server has to send a request to another server when it itself receives a request.

Any ideas how to fix this? Thanks.

It takes 40s because your requests are being processed in a synchronous way.
Never, EVER, do heavy lifting in the module ctor (static this / shared static this). All it is for is listenHTTP / setTimer / runTask and friends.

Regarding your memory problem, are you sure it's hard memory usage, and not just virtual ? By default each task (fiber) allocate some space, most of which will be unused (each one will/should basically be a page).
If it's hard memory, my guess is that the delegates are allocated. Try to make them functions and see if that improves things.

Re: requestHTTP performance

It takes 40s because your requests are being processed in a synchronous way.

I get that, but even so it's awfully slow. The same thing written in node.js using node-rest-client like this:

var Client = require('node-rest-client').Client; 
var client = new Client();

function doRequest(i)
{
    if (i%50 == 0) console.log(i);
    
    if (i < 1000) client.get("the same url", function(data, response){
        console.log(response);
        doRequest(i + 1);
    });
}

doRequest(0);

manages to do the same work in three to four seconds. And yes, it works synchronously as well.

Never, EVER, do heavy lifting in the module ctor (static this / shared static this). All it is for is listenHTTP / setTimer / runTask and friends.

Yes, I should have ran the test with runTask. Although that has no effect on the performance.

Regarding your memory problem, are you sure it's hard memory usage, and not just virtual ? By default each task (fiber) allocate some space, most of which will be unused (each one will/should basically be a page).
If it's hard memory, my guess is that the delegates are allocated. Try to make them functions and see if that improves things.

It is hard memory, but that's to be expected with a thousand tasks. Ultimately it doesn't really matter, as this is just a test. What matters is that requestHTTP takes a very long time.

By the way, I just noticed that when I do an HTTP request to a server like google.com, I get the following:

Task terminated with uncaught exception: Failed to lookup host 'google.com': nodename nor servname provided, or not known

What's up with that?

Re: requestHTTP performance

On Wed, 10 Jun 2015 18:48:30 GMT, Zdeněk wrote:

What's up with that?

The only way it can be so long is if you're disconnecting between requests. Not sure if you have a Connection: close header from the server or if the keepalive setting is being respected by vibe.d. Maybe you could enable vibe logs using import vibe.core.log; setLogLevel(LogLevel.trace); to see if there's a disconnect between requests.

Re: requestHTTP performance

The only way it can be so long is if you're disconnecting between requests. Not sure if you have a Connection: close header from the server or if the keepalive setting is being respected by vibe.d. Maybe you could enable vibe logs using import vibe.core.log; setLogLevel(LogLevel.trace); to see if there's a disconnect between requests.

I ran the program with --vvvv and I got this for each request:

[6806809C:6807399C trc] returning HTTPClient connection 0 of 1
[6806809C:6807399C trc] --------------------
[6806809C:6807399C trc] HTTP client request:
[6806809C:6807399C trc] --------------------
[6806809C:6807399C trc] GET <path goes here> HTTP/1.1
[6806809C:6807399C trc] User-Agent: vibe.d/0.7.23 (HTTPClient, +http://vibed.org/)
[6806809C:6807399C trc] Connection: keep-alive
[6806809C:6807399C trc] Accept-Encoding: gzip, deflate
[6806809C:6807399C trc] Host: localhost
[6806809C:6807399C trc] --------------------
[6806809C:6807399C trc] evbuffer_add (fd 9): 175 B
[6806809C:6807399C trc] HTTP client reading status line
[6806809C:6807399C trc] leastSize waiting for new data.
[6806809C:00000000 trc] socket 9 write event (false)!
[6806809C:00000000 trc] socket 9 read event!
[6806809C:6807399C trc] evbuffer_read 17 bytes (fd 9)
[6806809C:6807399C trc]  .. got 17 bytes
[6806809C:6807399C trc] read data
[6806809C:6807399C trc] stln: HTTP/1.1 200 OK
[6806809C:6807399C trc] evbuffer_read 47 bytes (fd 9)
[6806809C:6807399C trc]  .. got 47 bytes
[6806809C:6807399C trc] read data
[6806809C:6807399C trc] evbuffer_read 26 bytes (fd 9)
[6806809C:6807399C trc]  .. got 26 bytes
[6806809C:6807399C trc] read data
[6806809C:6807399C trc] evbuffer_read 37 bytes (fd 9)
[6806809C:6807399C trc]  .. got 37 bytes
[6806809C:6807399C trc] read data
[6806809C:6807399C trc] evbuffer_read 28 bytes (fd 9)
[6806809C:6807399C trc]  .. got 28 bytes
[6806809C:6807399C trc] read data
[6806809C:6807399C trc] evbuffer_read 31 bytes (fd 9)
[6806809C:6807399C trc]  .. got 31 bytes
[6806809C:6807399C trc] read data
[6806809C:6807399C trc] evbuffer_read 2 bytes (fd 9)
[6806809C:6807399C trc]  .. got 2 bytes
[6806809C:6807399C trc] read data
[6806809C:6807399C trc] ---------------------
[6806809C:6807399C trc] HTTP client response:
[6806809C:6807399C trc] ---------------------
[6806809C:6807399C trc] HTTP/1.1 200 OK
[6806809C:6807399C trc] Content-Type: application/json; charset=utf-8
[6806809C:6807399C trc] Server: Mono-HTTPAPI/1.0
[6806809C:6807399C trc] Date: Fri, 12 Jun 2015 14:51:06 GMT
[6806809C:6807399C trc] Transfer-Encoding: chunked
[6806809C:6807399C trc] Keep-Alive: timeout=15,max=23
[6806809C:6807399C trc] ---------------------
[6806809C:6807399C trc] read next chunk header
[6806809C:6807399C trc] evbuffer_read 5 bytes (fd 9)
[6806809C:6807399C trc]  .. got 5 bytes
[6806809C:6807399C trc] read data
[6806809C:6807399C trc] got chunk header: 9d8
[6806809C:6807399C trc] evbuffer_read 2520 bytes (fd 9)
[6806809C:6807399C trc]  .. got 2520 bytes
[6806809C:6807399C trc] read data
[6806809C:6807399C trc] evbuffer_read 2 bytes (fd 9)
[6806809C:6807399C trc]  .. got 0 bytes
[6806809C:00000000 trc] socket 9 read event!
[6806809C:6807399C trc] evbuffer_read 2 bytes (fd 9)
[6806809C:6807399C trc]  .. got 2 bytes
[6806809C:6807399C trc] read data
[6806809C:6807399C trc] read next chunk header
[6806809C:6807399C trc] evbuffer_read 3 bytes (fd 9)
[6806809C:6807399C trc]  .. got 3 bytes
[6806809C:6807399C trc] read data
[6806809C:6807399C trc] got chunk header: 0
[6806809C:6807399C trc] evbuffer_read 2 bytes (fd 9)
[6806809C:6807399C trc]  .. got 2 bytes
[6806809C:6807399C trc] read data
[6806809C:6807399C trc] returning HTTPClient connection 0 of 1


I think the connection is being reused here.

Re: requestHTTP performance

Yeah, I'm facing the same issue. I tried doing the same thing in C# (WebApi2) as well and it's at least tree times faster.
Any advice?

Best regards Jirka.

Re: requestHTTP performance

On Tue, 09 Jun 2015 20:01:41 GMT, Zdeněk wrote:

Any ideas how to fix this? Thanks.

I couldn't reproduce this. I tried alongside a WebRequest in C# and the result is the same, it's pretty much 2 to 4x the latency of the server multiplied by the number of requests. For a 40ms latency, you get 120ms per request for a download within 3 roundtrips, and then multiply by 1000 requests and you get a 120 seconds job. Obviously, if you use concurrent tasks it finishes within less than 1 second.

Re: requestHTTP performance

On Sun, 28 Jun 2015 12:48:58 GMT, Etienne Cimon wrote:

I couldn't reproduce this. I tried alongside a WebRequest in C# and the result is the same, it's pretty much 2 to 4x the latency of the server multiplied by the number of requests. For a 40ms latency, you get 120ms per request for a download within 3 roundtrips, and then multiply by 1000 requests and you get a 120 seconds job. Obviously, if you use concurrent tasks it finishes within less than 1 second.

I can reproduce this easily by setting up a local HTTP server using http-server serving a 13-bytes text file, and using the code above versus ab -k -c 1 -n 500 'http://127.0.0.1:8080/file.txt'.
ab finishes in 10 seconds while requestHTTP takes around 20 seconds.
I also noticed that when setting settings.defaultKeepAliveTimeout to 0.msecs, and running ab without keep-alive, the times fall closer to each other (and both are much faster finishing in less than 1 second! (most probably due to the server)).

Re: requestHTTP performance

On Fri, 07 Aug 2015 09:43:02 GMT, Yazan D wrote:

On Sun, 28 Jun 2015 12:48:58 GMT, Etienne Cimon wrote:

I couldn't reproduce this. I tried alongside a WebRequest in C# and the result is the same, it's pretty much 2 to 4x the latency of the server multiplied by the number of requests. For a 40ms latency, you get 120ms per request for a download within 3 roundtrips, and then multiply by 1000 requests and you get a 120 seconds job. Obviously, if you use concurrent tasks it finishes within less than 1 second.

I can reproduce this easily by setting up a local HTTP server using http-server serving a 13-bytes text file, and using the code above versus ab -k -c 1 -n 500 'http://127.0.0.1:8080/file.txt'.
ab finishes in 10 seconds while requestHTTP takes around 20 seconds.
I also noticed that when setting settings.defaultKeepAliveTimeout to 0.msecs, and running ab without keep-alive, the times fall closer to each other (and both are much faster finishing in less than 1 second! (most probably due to the server)).

Sorry if I'm being picky, you were doing 1000 requests with requestHTTP above, your ab -n 500 will make 500 requests. Did I understand correctly the parameters

Re: requestHTTP performance

Sorry if I'm being picky, you were doing 1000 requests with requestHTTP above, your ab -n 500 will make 500 requests. Did I understand correctly the parameters

It is ok. I should have been more clear. The number of requests for both is 500. I have to say that the test is not completely fair, because ab is using HTTP 1.0 and the requests are a little bit different. But I still believe that they are indicative of a problem somewhere.

Pages: 1 2