Note that there are some explanatory texts on larger screens.

plurals
  1. POpaste.httpserver and slowdown with HTTP/1.1 Keep-alive; tested with httperf and ab
    primarykey
    data
    text
    <p>I have a web server based on paste.httpserver as an adapater between HTTP and WSGI. When I do performance measurements with httperf, I can do over 1,000 requests per second if I start a new request each time using --num-conn. If I instead reuse the connection using --num-call then I get about 11 requests per second, 1/100th of the speed.</p> <p>If I try ab I get a timeout.</p> <p>My tests are</p> <pre><code>% ./httperf --server localhost --port 8080 --num-conn 100 ... Request rate: 1320.4 req/s (0.8 ms/req) ... </code></pre> <p>and</p> <pre><code>% ./httperf --server localhost --port 8080 --num-call 100 ... Request rate: 11.2 req/s (89.4 ms/req) ... </code></pre> <p>Here's a simple reproducible server</p> <pre><code>from paste import httpserver def echo_app(environ, start_response): n = 10000 start_response("200 Ok", [("Content-Type", "text/plain"), ("Content-Length", str(n))]) return ["*" * n] httpserver.serve(echo_app, protocol_version="HTTP/1.1") </code></pre> <p>It's a multi-threaded server, which is hard to profile. Here's a variation which is single threaded:</p> <pre><code>from paste import httpserver class MyHandler(httpserver.WSGIHandler): sys_version = None server_version = "MyServer/0.0" protocol_version = "HTTP/1.1" def log_request(self, *args, **kwargs): pass def echo_app(environ, start_response): n = 10000 start_response("200 Ok", [("Content-Type", "text/plain"), ("Content-Length", str(n))]) return ["*" * n] # WSGIServerBase is single-threaded server = httpserver.WSGIServerBase(echo_app, ("localhost", 8080), MyHandler) server.handle_request() </code></pre> <p>Profiling that with</p> <pre><code>% python2.6 -m cProfile -o paste.prof paste_slowdown.py </code></pre> <p>and hitting it with</p> <pre><code>%httperf --client=0/1 --server=localhost --port=8080 --uri=/ \ --send-buffer=4096 --recv-buffer=16384 --num-conns=1 --num-calls=500 </code></pre> <p>I get a profile like</p> <pre><code>&gt;&gt;&gt; p=pstats.Stats("paste.prof") &gt;&gt;&gt; p.strip_dirs().sort_stats("cumulative").print_stats() Sun Nov 22 21:31:57 2009 paste.prof 109749 function calls in 46.570 CPU seconds Ordered by: cumulative time ncalls tottime percall cumtime percall filename:lineno(function) 1 0.000 0.000 46.571 46.571 {execfile} 1 0.001 0.001 46.570 46.570 paste_slowdown.py:2(&lt;module&gt;) 1 0.000 0.000 46.115 46.115 SocketServer.py:250(handle_request) 1 0.000 0.000 44.675 44.675 SocketServer.py:268(_handle_request_noblock) 1 0.000 0.000 44.675 44.675 SocketServer.py:301(process_request) 1 0.000 0.000 44.675 44.675 SocketServer.py:318(finish_request) 1 0.000 0.000 44.675 44.675 SocketServer.py:609(__init__) 1 0.000 0.000 44.675 44.675 httpserver.py:456(handle) 1 0.001 0.001 44.675 44.675 BaseHTTPServer.py:325(handle) 501 0.006 0.000 44.674 0.089 httpserver.py:440(handle_one_request) 2001 0.020 0.000 44.383 0.022 socket.py:373(readline) 501 44.354 0.089 44.354 0.089 {method 'recv' of '_socket.socket' objects} 1 1.440 1.440 1.440 1.440 {select.select} .... </code></pre> <p>You can see that nearly all the time is in a recv.</p> <p>I decided to bail on httpref and write my own HTTP/1.1-with-keep-alive request and send it using netcat:</p> <pre><code>GET / HTTP/1.1 Location: localhost Connection: Keep-Alive Content-Length: 0 GET / HTTP/1.1 Location: localhost Connection: Keep-Alive Content-Length: 0 ... repeat 97 more times, to have 99 keep-alives in total ... GET / HTTP/1.1 Location: localhost Connection: Close Content-Length: 0 </code></pre> <p>which I sent with</p> <pre><code>nc localhost 8080 &lt; ~/src/send_to_paste.txt </code></pre> <p>Total time for 100 requests was 0.03 seconds, so it's very good performance.</p> <p>This suggests that httperf is doing something wrong (but it's a widely used and respected piece of code), so I tried 'ab'</p> <pre><code>% ab -n 100 -k localhost:8080/ This is ApacheBench, Version 1.3d &lt;$Revision: 1.73 $&gt; apache-1.3 Copyright (c) 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ Copyright (c) 2006 The Apache Software Foundation, http://www.apache.org/ Benchmarking localhost (be patient)... Server timed out : Operation now in progress </code></pre> <p>Instrumenting the server, it handles one request and is waiting for the second.</p> <p>Any idea of what's going on?</p>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload