#:g1: teepeedee2の紹介

Posted 2014-10-14 09:00:00 GMT

(LISP Library 365参加エントリ)

 LISP Library 365 の287日目です。

teepeedee2とはなにか

 teepeedee2は、John Fremlin氏作の高速なウェブアプリケーションのフレームワークです。

パッケージ情報

パッケージ名teepeedee2
Quicklisp
CLiKiCLiki: teepeedee2
Quickdocsteepeedee2 | Quickdocs
common-lisp.netteepeedee2 (tpd2)
CL Test Grid: ビルド状況teepeedee2 | CL Test Grid

インストール方法

(ql:quickload :teepeedee2)

試してみる

 どんな関数があるかは、Quickdocsで確認できます。

 約5年位前に世界最速として登場したteepeedee2ですが、当時熱かった最速競争は現在どうなっているのでしょう。
2009年末にULibというC++製のものに負けてしまってから特に動きはないようです。

2009年頃、Fremlin氏は日本に住んでいたためShibuya.lispでもteepeedee2の発表があったりしました。

ちなみに、このブログも2年前からteepeedee2で動いています。

 現在、Quicklispでインストールできますが、SBCL 1.2.4+最新のteepeedee2-20140713-gitを動かす上で、2点程ハマりどころがあります。

ですが、依存ライブラリが同梱されているのは、teepeedee2ディレクトリ直下のaddonsで、alexandriaや、cl-cont等が置かれていますが、これがあるとQuicklispで提供されているライブラリとごっちゃになってややこしいことになるので、自分は、addons以下をごっそり消して使っています。
次に、CFFIの書法の違いですが、

;;; teepeedee2-20140713-git/src/io/posix-socket.lisp
(defmethod socket-accept ((fd integer))
  (cffi:with-foreign-object (sa 'sockaddr_in)
    (cffi:with-foreign-object (len :int)
      (setf (cffi:mem-aref len :int) (cffi:foreign-type-size '(:pointer (:struct sockaddr_in))))
      (let ((s
             (socket-io-syscall
              #. (progn
                   (if (accept4-supported)
                    `(syscall-accept4 fd sa len
                                      (logior
                                       0
                                       #-tpd2-untransformed-io +SOCK_NONBLOCK+
                                       )
                                      )
                    `(syscall-accept fd sa len)
                    )))))
        (case-= s
                (-1 nil)
                (t
;                (socket-set-tcp-nodelay s)
;                (socket-cork s)

                 #.(unless (accept4-supported)
                     #-tpd2-untransformed-io
                     `(set-fd-nonblock s))

(make-con :socket s :peer-info (sockaddr-address-bv sa))))))))

(defmethod socket-recvfrom ( (fd integer) buf) (cffi:with-foreign-object (sa 'sockaddr_in) (cffi:with-foreign-object (len :int) (setf (cffi:mem-aref len :int) (cffi:foreign-type-size '(:pointer (:struct sockaddr_in)))) (with-pointer-to-vector-data (ptr buf) (let ((s (socket-io-syscall (syscall-recvfrom fd ptr (length buf) 0 sa len)))) (case-= s (-1 (values nil nil)) (0 (error 'socket-closed)) (t

(let ((sa-out (make-byte-vector (cffi:mem-aref len :int)))) (loop for i from 0 below (length sa-out) do (setf (aref sa-out i) (cffi:mem-ref sa :unsigned-char i))) (values s sa-out)))))))))

(defmethod socket-peer ((fd integer)) (cffi:with-foreign-object (sa 'sockaddr_in) (cffi:with-foreign-object (len :int) (setf (cffi:mem-aref len :int) (cffi:foreign-type-size '(:pointer (:struct sockaddr_in)))) (when (zerop (getpeername fd sa len)) (sockaddr-address-string sa)))))

;;; teepeedee2-20140713-git/src/io/syscalls.lisp (defun new-socket-helper (&key port address socket-family socket-type action) (let ((fd (syscall-socket socket-family socket-type 0))) (signal-protect (let ((network-port (htons port))) (setsockopt-int fd +SOL_SOCKET+ +SO_REUSEADDR+ 1) (set-fd-nonblock fd) (with-foreign-object-and-slots ((addr port family) sa (:struct sockaddr_in)) (setf family socket-family) (cffi:with-foreign-string (src address) (when (<= (inet_pton socket-family src (cffi:foreign-slot-pointer sa '(:struct sockaddr_in) 'addr)) 0) (error "Internet address is not valid: ~A" address))) (setf port network-port) (funcall action fd sa (cffi:foreign-type-size '(:struct sockaddr_in)))) fd) (syscall-close fd))))

こんな感じに直します(自信がないので間違っていたら教えて下さい)。
書法の違いについては、他の箇所でも警告が出ますが、致命的なのはこれだけなので、ここを修正すれば動かすことが可能です。

速度はどんなものなのか

 5年前に比べればマシンも速くなったことですし、このブログのページをローカルで動かして速度を計測してみます。

$ ab -n 100000 -c 10 'http://127.0.0.1:8200/3622147200'
This is ApacheBench, Version 2.3 <$Revision: 1604373 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 127.0.0.1 (be patient) Completed 10000 requests Completed 20000 requests Completed 30000 requests Completed 40000 requests Completed 50000 requests Completed 60000 requests Completed 70000 requests Completed 80000 requests Completed 90000 requests Completed 100000 requests Finished 100000 requests

Server Software: Server Hostname: 127.0.0.1 Server Port: 8200

Document Path: /3622147200 Document Length: 23193 bytes

Concurrency Level: 10 Time taken for tests: 6.297 seconds Complete requests: 100000 Failed requests: 0 Total transferred: 2327400000 bytes HTML transferred: 2319300000 bytes Requests per second: 15881.32 [#/sec] (mean) Time per request: 0.630 [ms] (mean) Time per request: 0.063 [ms] (mean, across all concurrent requests) Transfer rate: 360958.82 [Kbytes/sec] received

Connection Times (ms) min mean[+/-sd] median max Connect: 0 0 0.0 0 0 Processing: 0 1 0.8 1 85 Waiting: 0 1 0.8 1 85 Total: 0 1 0.8 1 85

Percentage of the requests served within a certain time (ms) 50% 1 66% 1 75% 1 80% 1 90% 1 95% 1 98% 1 99% 1 100% 85 (longest request)

$ ab -n 100000 -c 10 'http://127.0.0.1:8200/3620998320'
This is ApacheBench, Version 2.3 <$Revision: 1604373 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 127.0.0.1 (be patient) Completed 10000 requests Completed 20000 requests Completed 30000 requests Completed 40000 requests Completed 50000 requests Completed 60000 requests Completed 70000 requests Completed 80000 requests Completed 90000 requests Completed 100000 requests Finished 100000 requests

Server Software: Server Hostname: 127.0.0.1 Server Port: 8200

Document Path: /3620998320 Document Length: 12550 bytes

Concurrency Level: 10 Time taken for tests: 5.910 seconds Complete requests: 100000 Failed requests: 0 Total transferred: 1263100000 bytes HTML transferred: 1255000000 bytes Requests per second: 16919.21 [#/sec] (mean) Time per request: 0.591 [ms] (mean) Time per request: 0.059 [ms] (mean, across all concurrent requests) Transfer rate: 208697.85 [Kbytes/sec] received

Connection Times (ms) min mean[+/-sd] median max Connect: 0 0 0.0 0 0 Processing: 0 1 1.1 1 113 Waiting: 0 1 1.1 1 113 Total: 0 1 1.1 1 113

Percentage of the requests served within a certain time (ms) 50% 1 66% 1 75% 1 80% 1 90% 1 95% 1 98% 1 99% 1 100% 113 (longest request)

計測に利用したマシンは、Xeon E3-1230 v3 @ 3.30GHzのマシンです。

のページだと10万リクエストを6.297秒で処理(15880request/sec)。
ちょっと短かめの

のページだと、10万リクエストを5.910秒で処理(16920reqest/sec)でした。
これはローカルで直にアクセスした場合で、このブログようにリバースプロキシを利用すれば、それだけで大分速度は低下しますし、ネットの速度も大きく影響するので、さくらVPS上のこのブログのような場合、50request/secも出せてないかなと思います。

まとめ

 今回は、teepeedee2を紹介してみました。
ちなみに、このブログの年間PVはteepeedee2だと2秒程度で処理できます。

comments powered by Disqus