Friday, December 24, 2010

HipHop for PHP をインストールして apache で proxy してみた


依存関係が相当面倒なので、rpm が用意されている CentOS 5 にインストールすることにした。
CentoOS 5系であれば、以下のようなページも用意されている。
https://github.com/facebook/hiphop-php/wiki/Installing-or-Building-HipHop-PHP-via-RPM-on-CentOS-5
それでも相当いろんなパッケージを入れないといけない。

説明のとおり、yum の設定用パッケージなどを以下のとおり実行
[sikaku@localhost hiphop_rpm]$ sudo rpm -ivh http://epel.osuosl.org/5/x86_64/epel-release-5-4.noarch.rpm
[sikaku@localhost hiphop_rpm]$ sudo rpm -ivh http://dl.iuscommunity.org/pub/ius/stable/Redhat/5/x86_64/ius-release-1.0-6.ius.el5.noarch.rpm
[sikaku@localhost hiphop_rpm]$ sudo rpm -ivh http://pkg.tag1consulting.com/hphp/x86_64/hphp-release-1.0-2.el5.noarch.rpm
[sikaku@localhost hiphop_rpm]$ sudo rpm -Uvh http://download.fedora.redhat.com/pub/epel/5/x86_64/epel-release-5-3.noarch.rpm
[sikaku@localhost hiphop_rpm]$ sudo rpm -Uvh http://rpms.famillecollet.com/enterprise/remi-release-5.rpm
[sikaku@localhost hiphop_rpm]$ sudo rpm -Uvh http://centos.alt.ru/repository/centos/5/x86_64/centalt-release-5-3.noarch.rpm
[sikaku@localhost hiphop_rpm]$ sudo rpm -Uvh http://dag.wieers.com/rpm/packages/rpmforge-release/rpmforge-release-0.3.6-1.el5.rf.x86_64.rpm
[sikaku@localhost hiphop_rpm]$ sudo yum update
以下のとおり、大量に yum でパッケージを入れる。 何度もエラーと戦い続けて、以下が必要なことがわかった。
[sikaku@localhost hiphop_rpm]$ sudo yum install libidn-devel openssl-devel cvs cvsps gcc gcc-c++ libmcrypt zlib-devel automake libmcrypt mhash libtidy mysql
[sikaku@localhost hiphop_rpm]$ sudo yum install cmake mysql-devel pcre-devel gd-devel expat-devel libmcrypt-devel libcap-devel binutils-devel flex tbb-devel
以下に用意されているパッケージを全部ダウンロードする。
http://sourceforge.net/projects/hphp/files/CentOS%205%2064bit/
このダウンロードは、Irvine などでリンクインポートして一括ダウンロードすると良い。
mysqlのライブラリが古いと、エラーが出るので以下だけ、直接 mysql からダウンロードしてくる。
[sikaku@localhost hiphop_rpm]$ wget "http://dev.mysql.com/get/Downloads/MySQL-5.1/MySQL-shared-community-5.1.54-1.rhel5.x86_64.rpm/from/http://ftp.jaist.ac.jp/pub/mysql/"
で、以下のとおりインストール。一部ダウンロードしたファイルで、依存関係が問題で入らないのがあったので、除外している。
[sikaku@localhost hiphop_rpm]$ sudo rpm -Uvh hiphop-php-1.0-2.x86_64.rpm  boost-1.37.0-1.x86_64.rpm libicu-4.2.1-6.x86_64.rpm oniguruma-5.9.2-1.x86_64.rpm php52-5.2.12-1.ius.x86_64.rpm php52-cli-5.2.12-1.ius.x86_64.rpm php52-common-5.2.12-1.ius.x86_64.rpm tbb-2.2-1.20090809.x86_64.rpm php52-mysql-5.2.12-1.ius.x86_64.rpm MySQL-shared-community-5.1.54-1.rhel5.x86_64.rpm php52-pdo-5.2.12-1.ius.x86_64.rpm libxml2-*.rpm boost-devel-1.37.0-1.x86_64.rpm libevent-*.rpm php52-gd-5.2.12-1.ius.x86_64.rpm curl-devel-7.20.0-1hiphop.x86_64.rpm curl-7.20.0-1hiphop.x86_64.rpm icu-4.2.1-6.x86_64.rpm libicu-devel-4.2.1-6.x86_64.rpm oniguruma-devel-5.9.2-1.x86_64.rpm flex-2.5.35-7.x86_64.rpm
実際に実行してみた。vm環境で 256M しか割り当ててなかったからか、スワップしまくって7分もかかった。
[sikaku@localhost dev]$ cat test.php
<?php
 echo "hello world!";
?>
[sikaku@localhost dev]$ time hphp test.php
hello world!
real    7m8.916s
user    1m2.269s
sys     0m33.538s
[sikaku@localhost dev]$
ポート8999 で起動してみる。
[sikaku@localhost dev]$ mkdir bin
[sikaku@localhost dev]$ time hphp test.php --keep-tempdir=1 -o bin
hello world!
real    9m0.035s
user    1m5.599s
sys     0m34.827s
[sikaku@localhost dev]$ bin/program -m server -p 8999
Could not mlockall
loading static content...
loading static content took 0'00" (0 ms) wall time
page server started
admin server started
all servers started
以下のようにアクセスできる。
[sikaku@localhost ~]$ curl "http://localhost:8999/test.php"
hello world!
続いて、apache の設定を以下のように追加する。mod_rewrite か、mod_proxy で proxy 設定を書く。
[sikaku@localhost dev]$ cat /etc/httpd/conf.d/custom.conf
#RewriteEngine on
#RewriteRule /(.*) http://localhost:8999/$1 [P,L]
ProxyPassMatch ^/(.*)$ http://localhost:8999/$1
[sikaku@localhost dev]$ sudo /etc/init.d/httpd restart
80 ポートからアクセスできた。
[sikaku@localhost ~]$ curl "http://localhost/test.php"
hello world!

Sunday, December 5, 2010

libev を使った web server Lighttz を make してみた

まずは、libev のインストール

[192.168.162.128] cvs -z3 -d :pserver:anonymous@cvs.schmorp.de/schmorpforge co libev
[192.168.162.128] cd libev
[192.168.162.128] autoheader
[192.168.162.128] aclocal
[192.168.162.128] autoconf
[192.168.162.128] touch ltmain.sh
[192.168.162.128] automake -a
[192.168.162.128] ./configure --prefix=$HOME/local
[192.168.162.128] rm libtool
[192.168.162.128] ln -s /usr/bin/libtool libtool
[192.168.162.128] make
[192.168.162.128] make install
libtool がうまく動かなかったので置き換え lighttz の方では、以下のように Makefile を書き換えて gmake
[192.168.162.128] cat Makefile
lighttz: lighttz.o
        gcc -o lighttz ./lighttz.o -L$$HOME/local/lib -lev -u is_default_loop

lighttz.o: lighttz.c
        gcc -O3 -Wall -c -fmessage-length=0 -MMD -MP -MF"lighttz.d" -MT"lighttz.d" -I../libev -o $@ ./lighttz.c

clean:
        rm -f lighttz.o lighttz

start:
        LD_LIBRARY_PATH=/home/sikaku/local/lib/ ./lighttz &
起動してみる。
[192.168.162.128] LD_LIBRARY_PATH=/home/sikaku/local/lib/ ./lighttz &

[192.168.162.128] curl http://localhost:8002/
curl: (18) transfer closed with 1 bytes remaining to read
Hello World

Hello World

[192.168.162.128]
どうやら、1byte 書き出しが少ないようだ。
以下のように修正するとうまくいく
Index: lighttz.c
===================================================================
--- lighttz.c   (revision 103)
+++ lighttz.c   (working copy)
@@ -76,7 +76,7 @@
        char superjared[]="HTTP/1.1 200 OK\r\nContent-Length: 336\r\nConnection: close\r\nContent-Type: text/html\r\nDate: Sat, 26 Apr 2008 01:13:35 GMT\r\nServer: lighttz/0.1\r\n\r\n<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\"><html xmlns=\"http://www.w3.org/1999/xhtml\" version=\"-//W3C//DTD XHTML 1.1//EN\" xml:lang=\"en\"><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/><title>Hello World</title></head><body><p>Hello World</p></body></html>";

        if (revents & EV_WRITE){
-               write(cli->fd,superjared,strlen(superjared));
+               write(cli->fd,superjared,strlen(superjared)+1);
                ev_io_stop(EV_A_ w);
        }
        close(cli->fd);

Sunday, November 7, 2010

apache と nginx から node.js に proxy させる方法

node.js と nginx のインストールメモと、apache と nginx から proxy させる方法メモ

node.js のインストール

git からとってきて ./configure してみる

cd /home/sikaku/src
git clone git://github.com/ry/node.git
cd node
./configure
しかし、環境が古くて ./configure が通らない
../src/platform_linux.cc: In static member function `static void node::OS::SetProcessTitle(char*)':
../src/platform_linux.cc:29: error: `PR_SET_NAME' was not declared in this scope
Waf: Leaving directory `/home/sikaku/src/node/build'
Build failed:  -> task failed (err #1):
        {task: cxx platform_linux.cc -> platform_linux_4.o}
gmake: *** [all] Error 1
以下を見ると、ほぼ debug 用のようなので、
http://stackoverflow.com/questions/778085/how-to-name-a-thread-in-linux
以下のようなパッチをあててしのぐことにする
[192.168.162.128] diff -ru src/platform_linux.cc src/platform_linux.cc.NEW
--- src/platform_linux.cc       2010-10-22 11:09:49.000000000 +0900
+++ src/platform_linux.cc.NEW   2010-10-22 11:09:46.000000000 +0900
@@ -26,7 +26,7 @@
 void OS::SetProcessTitle(char *title) {
   if (process_title) free(process_title);
   process_title = strdup(title);
-  prctl(PR_SET_NAME, process_title);
+//  prctl(PR_SET_NAME, process_title);
 }
openssl が入っていなかったので、yum install しておく
sudo yum install openssl-devel
ようやく make install まで成功
./configure
make
sudo make install
以下のようなコードを書いて、実行してみる
[192.168.162.128] cat http.js
    var sys = require('sys'),
        http = require('http'),
        port = 8000; 
    http.createServer(function(request, response) {
        response.writeHead(200, {
            'Content-Type': 'text/html'
        });

        response.end( request.url + ' - Hello World\n');
    }).listen(port);
    sys.puts('Server listening on port ' + port);
起動してみる
node http.js &
アクセスしてみる
[192.168.162.128] curl "http://192.168.162.128:8000/test"
/test - Hello World

apache から node.js に proxy させる

rewrite で P をフラグをつければいいだけ
[192.168.162.128] cat /etc/httpd/conf.d/custom.conf
    RewriteEngine on
    RewriteRule /(.*) http://192.168.162.128:8000/$1 [P,L]
再起動
sudo //etc/init.d/httpd restart
apache にアクセスすると動いた。
[192.168.162.128] curl "http://192.168.162.128/test"
/test - Hello World

nginx のインストール

まったく問題なく make install まで成功した
cd /home/sikaku/src
wget "http://www.nginx.org/download/nginx-0.8.53.tar.gz"
tar zxvf nginx-0.8.53.tar.gz
cd nginx-0.8.53
./configure 
make
sudo make install
ポートを変えて起動してみる
[192.168.162.128] diff -ru /usr/local/nginx/conf/nginx.conf /usr/local/nginx/conf/nginx.conf.NEW
--- /usr/local/nginx/conf/nginx.conf    2010-10-22 13:38:25.000000000 +0900
+++ /usr/local/nginx/conf/nginx.conf.NEW        2010-10-22 13:38:33.000000000 +0900
@@ -33,7 +33,7 @@
     #gzip  on;

     server {
-        listen       80;
+        listen       8001;
         server_name  localhost;

         #charset koi8-r;
アクセスしてみる。
[192.168.162.128] curl "http://192.168.162.128:8001/"
<html>
<head>
<title>Welcome to nginx!</title>
</head>
<body bgcolor="white" text="black">
<center><h1>Welcome to nginx!</h1></center>
</body>
</html>

nginx から node.js に proxy させる

以下のように、nginx.conf を書き換えて、nginx を再起動する
[192.168.162.128] cat /usr/local/nginx/conf/nginx.conf | perl -nle 'print unless(/\s*#/ || /^\s*$/)'
worker_processes  1;
events {
    worker_connections  1024;
}
http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;
    server {
        listen       8001;
        server_name  localhost;
        location / {
            root   html;
            index  index.html index.htm;
        }
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
        location ~ .*$ {
            proxy_pass   http://192.168.162.128:8000;
        }
    }
}
問題なく動いた
[192.168.162.128] curl "http://192.168.162.128:8001/test"
/test - Hello World

Sunday, October 10, 2010

NICの設定を表示する: linux

NICの設定を確認する方法。

[sikaku@localhost ~]$ sudo /sbin/ethtool eth0
Settings for eth0:
        Supported ports: [ MII ]
        Supported link modes:   10baseT/Half 10baseT/Full
                                100baseT/Half 100baseT/Full
                                1000baseT/Full
        Supports auto-negotiation: Yes
        Advertised link modes:  10baseT/Half 10baseT/Full
                                100baseT/Half 100baseT/Full
                                1000baseT/Full
        Advertised auto-negotiation: Yes
        Speed: 100Mb/s
        Duplex: Full
        Port: MII
        PHYAD: 19
        Transceiver: external
        Auto-negotiation: on
        Supports Wake-on: g
        Wake-on: d
        Link detected: yes
ethtool は、表示と設定変更を行える
[sikaku@localhost ~]$ whatis ethtool
ethtool              (8)  - Display or change ethernet card settings
ethtool             (rpm) - Ethernet settings tool for PCI ethernet cards

Sunday, August 8, 2010

convert integer to std::string

int型変数を std::string に変換する方法を考えてみた。

C言語で、char * にするなら、反射的に、sprintf などを使うと思うが、 std::string に入れたいときに一番いい方法はあまり思いつかない。 今回は、いくつか案を考えて実際に家のPCで計測してみた。 以下のような関数をそれぞれ定義して、計測する。

void func(int num, std::string &str);

計測に使った関数は以下のとおり。 getMicroTime はマイクロタイムを取得する関数。

void testCount(const char *msg, int count, void (*func)(int, std::string &)){
    std::string str;
    int num;

    long start, ustart, end, uend;
    getMicroTime(start, ustart);
    for(int i=-count; i< count; i++){
        num = i;
        (*func)(num, str);
    }
    getMicroTime(end, uend);
    double i = (end - start) * 1000000;
    double f = uend - ustart;

    printf("%s time:%lf\n", msg, (i+f)/2/count );
}

[1]std::streamstring を使う

一番 C++ らしい方法はこれかもしれない。
void intToString1(int num, std::string &str){
    std::stringstream ss;
    ss << num;
    str = ss.str();
}
しかし、今回調べた中で最も遅かった。

[2]sprintf を使う

C言語のスタンダードな方法だと思う。
void intToString2(int num, std::string &str){
    char buf[32];
    sprintf(buf, "%d", num);
    str = buf;
}
[1]に比べたら、5~6倍ぐらい早かった

[3]10 のmodulo演算を使用

modulo 演算で、1の位から順に決定していく。
void intToString3(int num, std::string &str){
    int snum = num;
    if(num<0)
        snum = ~num + 1;
    char buf[12];
    char *ptr = buf+11;
    *ptr = '\0';
    while(snum > 0){
        *(--ptr) = '0' + snum % 10;
        snum /= 10;
    }
    if(num<0)
        *(--ptr) = '-';

    str = ptr;
}
[2]よりも、2~3倍速い

[4]長さを求めて、引数のstd::string に直接書き込む

作業用バッファを用意するのではなく、引数の std::string に直接書き込む方法とした。
void intToString4(int num, std::string &str){
    int snum = num & 0x80000000 ? ~num + 1 : num;

    int len = snum < 10000  ? (snum < 100 ? ( snum < 10 ? 1 : 2 ) : snum < 1000 ? 3 : 4)
         : snum < 100000000 ? (snum < 1000000 ? ( snum < 100000 ? 5 : 6 ) : snum < 10000000 ? 7 : 8)
         : snum < 1000000000 ? 9 : 10;

    if(num & 0x80000000)
        ++len;
    str.resize(len);

    while(snum > 0){
        str[--len] = '0' + snum % 10;
        snum /= 10;
    }
    if(num & 0x80000000)
        str[0] = '-';
}
[3]よりもだいたい 1.5 倍くらい早い

[5]std::string::data() で内部メモリに直接書く

[4]を修正し、str.data() で取得したアドレスに直接書いてみた。
    int snum = num & 0x80000000 ? ~num + 1 : num;

    int len = snum < 10000  ? (snum < 100 ? ( snum < 10 ? 1 : 2 ) : snum < 1000 ? 3 : 4)
         : snum < 100000000 ? (snum < 1000000 ? ( snum < 100000 ? 5 : 6 ) : snum < 10000000 ? 7 : 8)
         : snum < 1000000000 ? 9 : 10;
    if(num<0)
        ++len;
    str.resize(len);

    char *ptr = (char *)str.data()+len;
    while(snum > 0){
        *(--ptr) = '0' + snum % 10;
        snum /= 10;
    }
    if(num & 0x80000000)
        *(--ptr) = '-';
}
[4]より1.2倍くらい早くなった。

[6]再度sprintf

内部メモリに直接書く方法で、sprintf を使う方法にしてみた。
void intToString6(int num, std::string &str){
    int snum = num & 0x80000000 ? ~num + 1 : num;

    int len = snum < 10000  ? (snum < 100 ? ( snum < 10 ? 1 : 2 ) : snum < 1000 ? 3 : 4)
         : snum < 100000000 ? (snum < 1000000 ? ( snum < 100000 ? 5 : 6 ) : snum < 10000000 ? 7 : 8)
         : snum < 1000000000 ? 9 : 10;
    if(num<0)
        ++len;
    str.resize(len);

    char *ptr = (char *)str.data()+len;
    sprintf(ptr, "%d", num);
}
[5]に比べると、5倍~6倍くらい遅くなってしまった。

[7]大きい位の数から順に決めていく

長さを先に決めているので、大きい位から代入していくことにした。
void intToString7(int num, std::string &str){
    int snum = num & 0x80000000 ? ~num + 1 : num;

    int len = snum < 10000  ? (snum < 100 ? ( snum < 10 ? 1 : 2 ) : snum < 1000 ? 3 : 4)
         : snum < 100000000 ? (snum < 1000000 ? ( snum < 100000 ? 5 : 6 ) : snum < 10000000 ? 7 : 8)
         : snum < 1000000000 ? 9 : 10;
    if(num & 0x80000000)
        ++len;
    str.resize(len);

    char *ptr = (char *)str.data()-1;
    if( num & 0x80000000 ){
        *(++ptr) = '-';
         --len;
    }

    int one;
    if( len > 9 )
        one = snum / 1000000000, *(++ptr) = '0' + one, snum -= 1000000000 * one;
    if( len > 8 )
        one = snum / 100000000, *(++ptr) = '0' + one, snum -= 100000000  * one;
    if( len > 7 )
        one = snum / 10000000, *(++ptr) = '0' + one, snum -= 10000000 * one;
    if( len > 6 )
        one = snum / 1000000, *(++ptr) = '0' + one, snum -= 1000000 * one;
    if( len > 5 )
        one = snum / 100000, *(++ptr) = '0' + one, snum -= 100000 * one;
    if( len > 4 )
        one = snum / 10000, *(++ptr) = '0' + one, snum -= 10000 * one;
    if( len > 3 )
        one = snum / 1000, *(++ptr) = '0' + one, snum -= 1000 * one;
    if( len > 2 )
        one = snum / 100, *(++ptr) = '0' + one, snum -= 100  * one;
    if( len > 1 )
        one = snum / 10, *(++ptr) = '0' + one, snum -= 10 * one;

    *(++ptr) = '0' + snum;
}
これが、今回最も早い方法で、[5]よりも少し速くなった。

[8]少し改良

[7]を while を使って、キレイに書いてみた。
void intToString8(int num, std::string &str){
    static int tens[]={ 0, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000};
    int snum = num & 0x80000000 ? ~num + 1 : num;

    int len = snum < 10000  ? (snum < 100 ? ( snum < 10 ? 1 : 2 ) : snum < 1000 ? 3 : 4)
         : snum < 100000000 ? (snum < 1000000 ? ( snum < 100000 ? 5 : 6 ) : snum < 10000000 ? 7 : 8)
         : snum < 1000000000 ? 9 : 10;
    if(num & 0x80000000)
        ++len;
    str.resize(len);

    char *ptr = (char *)str.data();
    if(num & 0x80000000){
        *(ptr++) = '-';
         --len;
    }
    --ptr;

    int one = 0;
    int *tptr = &tens[len];
    while(*(--tptr)){
        one = snum / *tptr;
        *(++ptr) = '0' + one;
        snum -= *tptr * one;
    }
    *(++ptr) = '0' + snum;
}
なぜか、このコードは比較的遅いという結果になった。 配列tensから値を引くところが遅いようである。 [7]を配列tensを使うように変えると遅くなり、O2か、O3を指定すると[7]と同じ速度になった。

以上の計測結果は以下のとおり。

./out speed (最適化コンパイルなし )
intToString1 time:1.052384
intToString2 time:0.232779
intToString3 time:0.114349
intToString4 time:0.127272
intToString5 time:0.095429
intToString6 time:0.214056
intToString7 time:0.073023
intToString8 time:0.186148
./out1 speed ( -O コンパイルオプション )
intToString1 time:0.745957
intToString2 time:0.215731
intToString3 time:0.066834
intToString4 time:0.042813
intToString5 time:0.031119
intToString6 time:0.177850
intToString7 time:0.036448
intToString8 time:0.116570
./out2 speed ( -O2 コンパイルオプション )
intToString1 time:0.714182
intToString2 time:0.223773
intToString3 time:0.062432
intToString4 time:0.048148
intToString5 time:0.033870
intToString6 time:0.192560
intToString7 time:0.032088
intToString8 time:0.115626
./out3 speed ( -O3 コンパイルオプション )
intToString1 time:0.780855
intToString2 time:0.201429
intToString3 time:0.061252
intToString4 time:0.048085
intToString5 time:0.034723
intToString6 time:0.186857
intToString7 time:0.032486
intToString8 time:0.115503

こうしてみると、最速の[7] が、stringstream [1] よりも 20倍くらい異なる結果となった。 しかし単位はマイクロタイムなので、少し改良したところで大した差にはならない上、現状でもかなり速い。 改善するなら、全てビット演算だけで記述できればもっと改善できるかもしれない。

C言語(C++)で microtime を取得する

C言語で microtime を計算する方法が分からなかったので調べてみた。

#include <sys/time.h>

static long start, end;
void getMicroTime(long &sec, long &usec ){
    struct timeval now;
    int rv = gettimeofday(&now, 0);
    if (rv != 0){
        sec = 0;
        usec = 0;
        return;
    }
    sec = now.tv_sec;
    usec = now.tv_usec;
}
double getMicroTimeDouble(){
    struct timeval now;
    int rv = gettimeofday(&now, 0);
    if (rv != 0){
        return 0;
    }
    return (double)now.tv_sec + (double)now.tv_usec / 1000000;
}

Monday, August 2, 2010

zlib の deflate inflate sample

zlib を少し調べたので、std::string に対して、 deflate と inflate を行うクラスを作ってみた。

//ZlibUtil.h //ZlibUtilTest.cc
#include "ZlibUtil.h"

int main(int argc, char **argv)
{
    int ret;

    ZlibUtil zlib;

    std::string from, to;
    from = "あいうえおあいうえおあいうえおあいうえおあいうえお";
    ret = zlib.def(from, to, 9);
    printf("compress=%s %d\n", to.c_str(), to.length());

    ret = zlib.inf(to, from);
    printf("decompress=%s %d\n", from.c_str(), from.length());
}
[sikaku@localhost compress]$ ./zlibutil
compress=xレ{ワリア蚋cロ翦?])・27
decompress=あいうえおあいうえおあいうえおあいうえおあいうえお 75