15
13

More than 5 years have passed since last update.

H2O をいじくったタイルサーバの性能かくにん

Last updated at Posted at 2015-05-12

概略

先日投稿したH2OをいじったOpenStreetMap 用のタイルサーバの性能を測ってみた。

tl;dr

  • H2Oベースの実装は、既存の nginx ベースのタイルサーバより3倍速い
  • ただし、この性能差はソフトウェア ではなく、主にデータ形式の違いによるもの

実験条件

実行環境

  • 仮想4コア・メモリ8G の VMWare の仮想マシン
  • ゲスト OS は Debian 8.0: Linux debian 3.16.0-4-amd64 #1 SMP Debian 3.16.7-ckt9-3 (2015-04-23) x86_64 GNU/Linux
  • ホストは i7-4770K の Windows 8.1

nginxのパラメータチューニングとh2oパク参考に、net.* 系のカーネルパラメタを設定しておく。

比較対象

nginx に Lua をバインドして、タイルサーバ用のスクリプトを入れたもの。具体的には、OSM Japan が公開している tileman から、コードと設定を借りてくる。

nginx 自体は、ngx_openresty (Lua バインドが最初から入ってる) の最新版 (1.7.10.1) をソースで落として、gcc -mtune=native -march=native -O3 でビルドする:

$ wget http://openresty.org/download/ngx_openresty-1.7.10.1.tar.gz
$ export CFLAGS="-O3 -mtune=native -march=native"
#sudo so that /sbin/ldconfig is visible
$ sudo ./configure --with-luajit --with-cc=gcc --with-cc-opt="$CFLAGS" --with-luajit-xcflags="$CFLAGS" --prefix=/usr --conf-path=/etc/nginx/nginx.conf
$ sudo make -j 4
$ sudo make install

実験方法

リクエストするURL

あらかじめレンダリングが済んだ東京周辺の地図に対して、ズームレベル 0~17 までのタイル1 (約77万ファイル) をひたすらリクエストする:
image

タイルの URL (の、パス名部分) はこんな感じだ:

$ cat paths.txt
/tiles/0/0/0.png
/tiles/1/1/0.png
/tiles/2/3/1.png
/tiles/3/7/3.png
...
/tiles/17/116822/51974.png
/tiles/17/116823/51974.png
/tiles/17/116824/51974.png
$ wc -l paths.txt
770534 paths.txt

負荷のかけ方

wrk に以下のスクリプトを食わせて、リストファイルのパスを順番にフェッチさせていく:

-- traverse.lua
paths = io.lines("paths.txt")
request = function()
    p = paths()
    if (p == nil) then
      paths = io.lines("paths.txt")
      p = paths()
    end
    return wrk.format(nil, p)
end

$ wrk --latency -c 1000 -t 4 -d 10 -s traverse.lua http://localhost:8080/

なお、wrk を別ホストにするのもやってみたが、VLAN帯域を食いつぶしてマトモな測定にならなかったので、サーバと同じホストで動かすことにした。

タイルデータの形式

H2O でサーブするタイルデータは、各タイルが個別の .png ファイルになっている。つまり、今回の場合だと、約77万個の .png が特定のディレクトリの下に存在していることになる (もちろん、階層はフラットではないが)。

一方、nginx の実装は、「メタタイル」といって、8x8 = 64 枚の .png を tar のようにくっつけた形式のファイルを使う。
このくっつけたメタタイルの中から、「しかるべき .png 一枚分」を切り取ってクライアントに返すわけだ。

というわけで、H2O用の .png 77万枚に加えて、それと等価な nginx 用のメタタイルを用意した。
メタタイルの作り方は割愛するが、mod_tile という Apache モジュールに付属のオフラインレンダラーを使う。

なお、公平のため、上記2種類のデータセットは同じファイルシステムに置いた。

サーバソフトウェアの設定

各サーバソフトの設定は、以下のようにした。これも比較記事の設定を大いにパク参考にした。

H2O

tiles.conf

# to find out the configuration commands, run: h2o --help
num-threads: 8
#num-name-resolution-threads: 1
max-connections: 10240
mapnik-datasource: /usr/local/lib/mapnik/input
mapnik-fonts: /usr/local/lib/mapnik/fonts
mapnik-fonts: /usr/share/fonts

listen: 8080
hosts:
  "127.0.0.1.xip.io:8080":
    paths:
      /tiles:
        tile.dir: /opt/osm/tiles
        tile.style: /opt/osm/openstreetmap-carto/osm.xml
        expires: 1 day
      /:
        file.dir: /opt/osm/www
    access-log: /dev/null
#    access-log: /opt/osm/log/access_log
  • スレッド数 = 8
  • 最大コネクション = 10240
  • アクセスログはとらない
  • タイルは /opt/osm/tiles の下にあるよ

というのが大事で、あとはわりとどうでもいい。

nginx

/etc/nginx/nginx.conf

worker_processes  8;

events {
    worker_connections  10240;
    accept_mutex_delay 100ms;
}

http {
lua_package_path "/usr/local/lib/lua/?.lua";
    include       mime.types;
    default_type  application/octet-stream;

    access_log off;

    sendfile on;
    open_file_cache max=100 inactive=20s;
    tcp_nopush on;

    keepalive_timeout  65;

    server {
        listen       80;
        server_name  localhost;
        location / {
            root   /opt/osm/www;
            index  index.html;
        }

        location /tiles {
            access_by_lua '
              local osm_tile = require "osm.tile"
              local tirex = require "osm.tirex"
              local map = ""
              local x, y, z = osm_tile.get_cordination(ngx.var.uri, "/tiles", "png")
              -- DO NOT ask tirex to render it (tirex is not set-up properly)
              --local priority = 1
              --local ok = tirex.request(map, x, y, z, z, priority)
              --if not ok then
              --    return ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
              --end
              local tilefile = osm_tile.xyz_to_metatile_filename(x, y, z)
              local tilepath = "/opt/osm/ng_tiles/"..map.."/"..tilefile
              local png, err = osm_tile.get_tile(tilepath, x, y, z)
              if png then
                  ngx.header.content_type = "image/png"
                  ngx.print(png)
                  return ngx.OK
              end
              return ngx.exit(ngx.HTTP_NOT_FOUND)
            ';
            root /opt/osm/tiles/;
        }   
    }
}

スレッド数などは当然 H2O と合わせてある。
本題は access_by_lua のところで、ざっくり言うと

  1. リクエストURLをパーズして x, y, z (座標とズームレベル) を求めよ
  2. それを元に、しかるべきメタタイルの、しかるべき部分を切り出せ
  3. 切り出した画像を送り返せ
  4. メタタイルがなかったり、切り出せなかったら単に 404 を返せ。レンダラーに仕事を投げるとか余計なことは今回はしなくてもいい

ということをやっている。
本来は、メタタイルがなかったらバックエンドのレンダラーにお仕事を投げるのだが、今回、そのバックエンドを用意できなかったので該当箇所をコメントアウトした。
余計な仕事をコメントアウトしただけなので、これで nginx 不利になったりはしないハズだ。

結果

$ wrk --latency -c 1000 -t 4 -d 10 -s ... を、H2O と nginx それぞれに対して 10 回ずつ測定した。10回の中で最も結果が良かったものを下に載せる:

H2O

Running 10s test @ http://localhost:8080/
  4 threads and 1000 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     4.08ms    4.70ms 210.40ms   91.20%
    Req/Sec    38.46k    12.43k   99.92k    77.72%
  Latency Distribution
     50%    2.98ms
     75%    5.07ms
     90%    8.32ms
     99%   18.54ms
  1535421 requests in 10.07s, 7.11GB read
Requests/sec: 152439.26
Transfer/sec:    722.99MB

152k req./秒 ぐらい。

nginx

Running 10s test @ http://localhost/
  4 threads and 1000 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    92.52ms  232.91ms   1.75s    91.27%
    Req/Sec    11.69k     5.73k   28.45k    67.00%
  Latency Distribution
     50%    5.69ms
     75%   48.56ms
     90%  257.60ms
     99%    1.17s 
  466463 requests in 10.03s, 3.20GB read
  Socket errors: connect 0, read 0, write 0, timeout 271
Requests/sec:  46499.53
Transfer/sec:    327.15MB

46.5k req./秒 ぐらい。

考察

というわけで、今回の測定では、H2O をいじったタイルサーバは、既存の nginx の実装を3倍ぐらい outperform しており、実際スゴイ。
ただし、この差は ソフトウェアの性能差ではなく、ただの .png とメタタイルという データ形式の違い が大きいと考えられる。
「考えられる」といいつつ実証とかしてないのだが・・・メタタイルを毎回 fseek() して切り取って・・・っていかにも遅そうやん?

まとめ

  • メタタイルを使わないタイルサーバはすごく速くできる、可能性がある
  • じゃあ、メタタイルっていらないんじゃ・・・

なぜ「メタタイル」という一見余計なレイヤーが標準的になったのか、は、機会があれば解説する。明日から頑張る。本当だよ。


  1. 今更だが、「タイル」というのは地図の一部分を表す画像データだ。大抵は 256x256 ピクセルの PNG を使う。この PNG をブラウザ側でがんばってずらっと縦横に並べることで、地図として見えるわけだ。 

15
13
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
13