From b6652079e1bdebe0cc6e37aebbc71bf9d45f6b94 Mon Sep 17 00:00:00 2001 From: Kazu Yamamoto Date: Thu, 1 Nov 2012 14:42:48 +0900 Subject: [PATCH] writing "header and body" section. --- tcpdump.graffle | Bin 0 -> 2709 bytes tcpdump.png | Bin 0 -> 31849 bytes warp.html | 36 ++++++++++++++++---------- warp.md | 67 +++++++++++++++++++++++++++++++++++++++--------- 4 files changed, 78 insertions(+), 25 deletions(-) create mode 100644 tcpdump.graffle create mode 100644 tcpdump.png diff --git a/tcpdump.graffle b/tcpdump.graffle new file mode 100644 index 0000000000000000000000000000000000000000..75155f6d1dd4c8989cd09c11de6d34c797b04602 GIT binary patch literal 2709 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 P^`-v;%9sxpd{qDdYMw`b literal 0 HcmV?d00001 diff --git a/tcpdump.png b/tcpdump.png new file mode 100644 index 0000000000000000000000000000000000000000..9b936d306167201dbae04aef5b0fa4fc8101de24 GIT binary patch literal 31849 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 G4gNprA#`W} literal 0 HcmV?d00001 diff --git a/warp.html b/warp.html index e6c8106..e5db6b1 100644 --- a/warp.html +++ b/warp.html @@ -38,18 +38,18 @@

Warp's architecture

The type of WAI applications is as follows:

type Application = Request -> ResourceT IO Response

In Haskell, argument types of function are separated by right arrows and the most right one is the type of return value. So, we can interpret the definition as an application takes Request and returns Response.

-

After accepting a new connection, a dedicated user thread is spawn for the connection. It first receives an HTTP request from a client and parses it to Request. Then, Warp gives the Request to an application and takes a Response from it. Finally, Warp builds an HTTP response based on Response and sends it back to the client. This is illustrated in Fix XXX.

+

After accepting a new HTTP connection, a dedicated user thread is spawn for the connection. It first receives an HTTP request from a client and parses it to Request. Then, Warp gives the Request to an application and takes a Response from it. Finally, Warp builds an HTTP response based on Response and sends it back to the client. This is illustrated in Fix XXX.

Warp

Warp

-

The user thread repeats this procedure and terminates by itself when the connection is closed by the peer.

+

The user thread repeats this procedure if necessary and terminates by itself when the connection is closed by the peer.

Performance of Warp

-

Before we explain how to improve the performance of Warp, we would like to show the results of our benchmark. We measured throughput of Mighttpd 2.8.2 and nginx 1.2.4. Our benchmark environment is as follows:

+

Before we explain how to improve the performance of Warp, we would like to show the results of our benchmark. We measured throughput of Mighttpd 2.8.2 (with Warp x.x.x) and nginx 1.2.4. Our benchmark environment is as follows:

  • One "12 cores" machine (Intel Xeon E5645, two sockets, 6 cores per 1 CPU, two QPI between two CPUs)
  • Linux version 3.2.0 (Ubuntu 12.04 LTS), which is running directly on the machine (i.e. without a hypervisor)
-

We tested several benchmark tools in the past and our favorite one is weighttp. It is based on the epoll system call family and can use multiple native threads. We used weighttp as follows:

+

We tested several benchmark tools in the past and our favorite one was httperf. Since it uses the select() system call and is just a single process program, it reaches its performance limits when we try to measure HTTP servers on multi-cores. So, we switched to weighttp, which is based on the epoll system call family and can use multiple native threads. We used weighttp as follows:

weighttp -n 100000 -c 1000 -t 3 -k http://127.0.0.1:8000/

This means that 1,000 HTTP connections are established and each connection sends 100 requests. 3 native threads are spawn to carry out these jobs..

For all requests, the same index.html file is returned. We used nginx's index.html whose size is 151 bytes. As "127.0.0.1" suggests, We measured web servers locally. We should have measured from a remote machine but we don't have suitable environment at this moment. (NOTE: I'm planning to do benchmark using two machines soon.)

@@ -65,16 +65,21 @@

Performance of Warp

Performance of Warp and nginx

Performance of Warp and nginx

X-axis is the number of workers and y-axis means throughput whose unit is requests per second.

-

Lesson learned

+

Key ideas

+

There are three key ideas to implement high-performance server in Haskell:

  1. Issuing as few system calls as possible
  2. Specialization and avoiding re-calculation
  3. Avoiding locks
+

If a system call is issued, CPU time is given to kernel and all user threads stop. So, we need to use as fewe system calls as possible. For a HTTP session to get a static file, Warp calls recv(), send() and sendfile() only (Fig warp.png). open(), stat(), close() and other system calls can be committed thanks to cache mechanism described later.

+

TBD

+

TBD

+

To make our explanation simple, we will talk about Linux only for the rest of this article.

HTTP request parser

  • Parser generator vs handmade parser
  • -
  • From "Warp: A Haskell Web Server"?
  • +
  • No timeout care thanks to timeout manager -- From "Warp: A Haskell Web Server"?
  • Conduit

HTTP response builder

@@ -90,15 +95,19 @@

response body

  • sendfile
  • sending header and body together

    -
      -
    • http://www.yesodweb.com/blog/2012/09/header-body
    • -
    +

    When we measured the performance of Warp, we always did it with high concurrency. That is, we always make multiple connections at the same time. It gave us a good result. However, when we set the number of concurrency to 1, we found Warp is really really slow.

    +

    We realized that this is because Warp uses the combination of writev() for header and sendfile() for body. In this case, an HTTP header and body are sent in separate TCP packets (Fig xxx).

    +
    +Packet sequence of old Warp

    Packet sequence of old Warp

    +
    +

    To send them in a single TCP packet (when possible), we switched from writev() to send(). We use the send() system call with the MSG_MORE flag to store a header and the sendfile() system call to send both the stored header and a file. This made the throughput at least 100 times faster.

    Clean-up with timers

    For connections

    • Requirements
    • -
    • System.Timeout.timeout
    • -
    • MVar vs IORef
    • +
    • System.Timeout.timeout (not scale because one timeout thread per thread)
    • +
    • MVar (slow because homebrew spin lock is used)
    • +
    • IORef
    • Its algorithm

    Need a fig

    @@ -108,7 +117,7 @@

    For file descriptors

  • Red black tree
  • Need a fig

    -

    Logging

    +

    Logging (xxx necessary?)

    • Handle
    • From the Mighty article in Monad.Reader
    • @@ -126,9 +135,10 @@

      Other tips

    • pessimistic read

    Profiling and benchmarking

    +

    Each item should be included in other chapters.

      +
    • weighttp (done)
    • GHC profiler
    • -
    • httperf/weighttp
    • strace
    • eventlog
    • prof
    • diff --git a/warp.md b/warp.md index ac2dc2c..73a9bc7 100644 --- a/warp.md +++ b/warp.md @@ -130,7 +130,7 @@ the most right one is the type of return value. So, we can interpret the definition as an application takes `Request` and returns `Response`. -After accepting a new connection, a dedicated user thread is spawn for the +After accepting a new HTTP connection, a dedicated user thread is spawn for the connection. It first receives an HTTP request from a client and parses it to `Request`. @@ -142,22 +142,26 @@ This is illustrated in Fix XXX. ![Warp](warp.png) -The user thread repeats this procedure and terminates by itself when +The user thread repeats this procedure if necessary and terminates by itself when the connection is closed by the peer. ## Performance of Warp Before we explain how to improve the performance of Warp, we would like to show the results of our benchmark. -We measured throughput of Mighttpd 2.8.2 and nginx 1.2.4. +We measured throughput of Mighttpd 2.8.2 (with Warp x.x.x) and nginx 1.2.4. Our benchmark environment is as follows: - One "12 cores" machine (Intel Xeon E5645, two sockets, 6 cores per 1 CPU, two QPI between two CPUs) - Linux version 3.2.0 (Ubuntu 12.04 LTS), which is running directly on the machine (i.e. without a hypervisor) We tested several benchmark tools in the past and -our favorite one is `weighttp`. -It is based on the `epoll` system call family and can use +our favorite one was `httperf`. +Since it uses the `select()` system call and is just a single process program, +it reaches its performance limits when we try to measure HTTP servers on +multi-cores. +So, we switched to `weighttp`, which +is based on the `epoll` system call family and can use multiple native threads. We used `weighttp` as follows: @@ -197,16 +201,34 @@ Here is the result: X-axis is the number of workers and y-axis means throughput whose unit is requests per second. -## Lesson learned +## Key ideas + +There are three key ideas to implement high-performance server in Haskell: 1. Issuing as few system calls as possible 2. Specialization and avoiding re-calculation 3. Avoiding locks +If a system call is issued, +CPU time is given to kernel and all user threads stop. +So, we need to use as fewe system calls as possible. +For a HTTP session to get a static file, +Warp calls `recv()`, `send()` and `sendfile()` only (Fig warp.png). +`open()`, `stat()`, `close()` and other system calls can be committed +thanks to cache mechanism described later. + +TBD + +TBD + +To make our explanation simple, we will talk about Linux only +for the rest of this article. + ## HTTP request parser - Parser generator vs handmade parser -- From "Warp: A Haskell Web Server"? +- No timeout care thanks to timeout manager +-- From "Warp: A Haskell Web Server"? - Conduit ## HTTP response builder @@ -224,15 +246,34 @@ whose unit is requests per second. ### sending header and body together -- http://www.yesodweb.com/blog/2012/09/header-body + +When we measured the performance of Warp, +we always did it with high concurrency. +That is, we always make multiple connections at the same time. +It gave us a good result. +However, when we set the number of concurrency to 1, +we found Warp is really really slow. + +We realized that this is because Warp uses +the combination of writev() for header and sendfile() for body. +In this case, an HTTP header and body are sent in separate TCP packets (Fig xxx). + +![Packet sequence of old Warp](tcpdump.png) + +To send them in a single TCP packet (when possible), +we switched from `writev()` to `send()`. +We use the `send()` system call with the `MSG_MORE` flag to store a header +and the `sendfile()` system call to send both the stored header and a file. +This made the throughput at least 100 times faster. ## Clean-up with timers ### For connections - Requirements -- System.Timeout.timeout -- MVar vs IORef +- System.Timeout.timeout (not scale because one timeout thread per thread) +- MVar (slow because homebrew spin lock is used) +- IORef - Its algorithm Need a fig @@ -248,7 +289,7 @@ Need a fig Need a fig -## Logging +## Logging (xxx necessary?) - Handle - From the Mighty article in Monad.Reader @@ -267,8 +308,10 @@ Need a fig ## Profiling and benchmarking +Each item should be included in other chapters. + +- weighttp (done) - GHC profiler -- httperf/weighttp - strace - eventlog - prof