Gitのちょっと便利な使い方

はてなブックマーク -Gitのちょっと便利な使い方
こんにちは
kaoruです。

今回は、GitのちょっとしたTipsをご紹介したいと思います。

Gitというのは、近年、Subversionに代わって普及が進んできたバージョン管理システムです。

特徴として、Subversionに比べて高速である点と、マージの管理が簡単である点が挙げられます。

規模の大きな開発や、複数人での開発を行っていると、たくさんのブランチが発生して、ブランチ間の関係をビジュアルに確認したくなることが頻繁にあります。

TortoiseGitやQGitなどのGUIツールを使うとブランチ間の関係をビジュアルに表示することができます。

しかし、普段コマンドラインで作業している場合、わざわざ別のツールを使うのではなくコマンドラインで直接確認できた方が便利なのではないでしょうか。

実は、git logコマンドにはオプションとして、--graphがあり、標準でもツリー表示ができます。しかし、そのままでは、表示される情報が多すぎて、TortoiseGitのような一覧性がよくありません。

そこで、オプションを工夫してコマンドラインでもビジュアルにブランチ間のツリー構造がビジュアルに確認できるようにしてみました。

git log --graph --date-order -C -M --pretty=format:\"<%h> %ad [%cn] %Cgreen%d%Creset %s\" --all --date=short

これを毎回、入力するのはいささか大変ですので、エイリアス機能を使っています。

以下のような設定を $HOME/.gitconfig か、/etc/gitconfig に追加します。
[alias]
    graph = log --graph --date-order -C -M --pretty=format:\"<%h> %ad [%cn] %Cgreen%d%Creset %s\" --all --date=short
(2行目は途中で改行しないようにしてください。)

このような設定をしておけば、
git graph
と入力するだけで、表示可能です。
yumewaza-git-screen-capture.gif

この表示ができると、日々の作業がとても便利になります。
Gitをお使いの方は、是非一度お試しください。
タグ: |
日時: 2010年07月30日 18:38 | コメント (0) | トラックバック (0)

ファイルディスクリプタ数の上限変更とlimits.confの罠

はてなブックマーク -ファイルディスクリプタ数の上限変更とlimits.confの罠


こんにちは、mikkoです。

今回は、1プロセスが同時オープン可能なファイルディスクリプタ数の上限を変更する方法と、その際の落とし穴についてです。

Linuxでは、同時にオープンできるファイルディスクリプタ数が制限されています。
OS全体での設定は、/proc/sys/fs/file-max等で確認でき、大規模なアクセスがあるサーバでは/etc/sysctl.confに設定を追加して上限を増やしているケースも多いでしょう。

これとは別に、1プロセスが同時オープン可能なファイルディスクリプタ数は、標準で1024となっています。
apacheのpreforkモデル等の子プロセスが多数稼動するアプリケーションだと、1プロセスあたりのファイルオープン数はそこまで増加しないので、普段はあまり意識することは少ないかもしれません。
しかし、apacheのworkerモデル等のスレッドを使用するアプリケーションでは、この制限が致命的になるケースが発生します。

では、このファイルディスクリプタ数上限を上げるにはどうすればよいのでしょうか。

1プロセスあたりのファイルディスクリプタ数上限を上げるには3通りあり、

  1. カーネルヘッダファイル中のINR_OPENを書き換えてrebuild
  2. ulimit -n で一時的に更新する
  3. /etc/security/limits.conf に設定を記述する

の何れかを選択することになりますが、カーネルのrebuildは手間なので除外すると、選択肢は2つになります。

※プログラム内からsetrlimit()システムコールを呼ぶ方法もありますが、今回は既存のプログラムを扱うことを前提としているので除外します

ulimitとは何なのか?

シェルのbuilt-inコマンドで、プロセスに割り当てる各種リソースの制限を行うためのものです(csh系はlimitコマンド)。
ulimit -n を実行すると、現在のファイルディスクリプタ数上限値(SoftLimitおよびHardLimit)が確認できます。
SoftLimitはHardLimit以下の範囲内で一般ユーザが変更可能ですが、HardLimitはrootでしか変更できません。
ulimitを実行したシェルおよび、そのシェルから起動される子プロセスに対して一時的に設定を変えることが出来ます。

/etc/security/limits.conf とは何なのか?

/etc/security/limits.confは、PAM認証モジュールの1つであるpam_limitsの設定ファイルです。

<domain> <type> <item> <value>

というフォーマットで記述を行い、

root soft nofile 2048
root hard nofile 2048

と書けば、rootで実行されるプロセス単位の最大ファイルオープン数が2048に設定されます。

pam_limitsモジュールは、PAMの設定ファイル/etc/pam.d/system-auth内においてsession型で定義されており、/etc/pam.d/loginや/etc/pam.d/sshd、/etc/pam.d/sudo等多くの設定ファイルがsystem-authをインクルードする形で使用しています。
従って、先の設定はログイン時やsudo時など、PAM認証が発生する(且つsessionでpam_limitsモジュールがrequireされる)タイミングで有効化されます。

つまり、「PAM認証を介さないようなdaemon系プログラムの制限には/etc/security/limits.confは使えない」ことになるのです。

ところが、googleで「apache limits.conf」等を検索すると、limits.confを書き換えればapacheの上限ファイルディスクリプタ数を増加可能という内容の記事が散見されます。 これはいったいどういうことなのでしょうか?

罠1:該当ユーザでのulimit -nによる確認

limits.confに

* soft nofile 2048
* hard nofile 2048

を記述し、su等でrootになり、ulimit -nを実行して変更確認を行う方法です。

しかし、ログイン時やsu時においてPAM認証が行われた時点でlimits.confの設定が適用されるため、rcスクリプトやdaemontoolsから起動されるプロセスと環境が異なり、確認の意味を成していません。

罠2:rcスクリプトによる再起動での確認

実際にdaemonの再起動を行い、'Too many open files'のエラーが出なくなったことを確認する方法です。

実は、rcスクリプトやapachectl等で再起動を行うと、実際に設定が反映されています。
これは非常にわかりにくく、以下のような状況が発生しています。

例:sudo /etc/init.d/httpd restart

  1. sudoでrootユーザになる際にPAM認証が行われ、limits.confの設定が適用される(ulimit -n 2048と同義)
  2. rootユーザで/etc/init.d/httpdを実行する際、上限値が子プロセス(/etc/init.d/httpdはスクリプトなので、bashになる)に引き継がれる
  3. スクリプト内でhttpdを停止・開始する際、上限値が子プロセス(開始されるhttpd)に引き継がれる
  4. 上限値が引き継がれたhttpdが開始される

実際に以下の手順で検証してみました。

  1. 1100個のファイルを同時オープンする簡単なphpスクリプト(index.php)を用意
  2. <?php
    $fp = array();
    for ($i = 0; $i < 1100; $i++) {
        $fp[] = fopen('index.php', 'r');
    }
    echo '<pre>';
    var_dump($fp);
    echo '</pre>';
    
  3. apache経由でアクセスし、1000個付近でファイルが開けなくなることを確認
  4. ...snip...
    [999]=>
    resource(1002) of type (stream)
    [1000]=>
    resource(1003) of type (stream)
    [1001]=>
    bool(false)
    [1002]=>
    bool(false)
    ...snip...
    
    ※ apacheのerror_logにも、'Too many open files'が表示されています。
    ※ 上限の1024に満たないのは、httpdプロセス自身がいくつか消費しているためです。lsofコマンド等で確認が可能です。
  5. limits.conf を書き換える
  6. * soft nofile 2048
    * hard nofile 2048
    

    を追加する。

  7. sudo /etc/init.d/httpd restartでapacheを再起動する
  8. 再度apache経由でアクセスし、1000個以上ファイルが開けることを確認
  9. ※ ここまでで、上限値が反映されたと誤解してしまいます!
  10. マシンを再起動する
  11. 三度apache経由でアクセスし、1000個付近でファイルが開けなくなることを確認
  12. ※ 上限値が1024に戻ってしまいます!

つまり、手動で再起動した場合は一時的にlimits.confの設定内容が有効になるだけなのです。
当然、マシン自体が再起動した場合はinitが各daemonを起動し、PAM認証が入らないため、OS規定の上限値である1024に戻ってしまいます。

結局、リミット値を上げるには、ulimit -n の記述を

  • daemontoolsであれば、/service/<サービス名>/runファイルに追加
  • rcスクリプトであれば、/etc/init.d/<サービス名>ファイルに追加

しないといけません。

ただしapacheの場合、apachectl経由での制御を行うとrcスクリプトを介さないため、apachectlにも追加しておかないといけないことに注意してください。

  • apachectl restart
  • 既存のhttpdプロセスにシグナルを送り、既存httpdから新規httpdがforkされるため、既存httpdのリミット値が引き継がれます。

  • apachectl start
  • 新規httpdが起動され、上記rcスクリプトの実行の流れと同じ現象が起こるため、ulimitの追加が必要です。

結論

daemon系プロセスのファイルディスクリプタ数上限を設定する際、/etc/security/limits.conf は使えません。状況によっては一見設定されたように見えますが、大きな落とし穴にはまることになります。
面倒ですが、必要なプロセス毎にulimitを用いて適切に設定しましょう。

タグ: | |
日時: 2010年07月26日 17:47 | コメント (0) | トラックバック (0)

コンパイル言語 C++ とスクリプト言語 Lua を、Luabind を使って組み合わせてみる

はてなブックマーク -コンパイル言語 C++ とスクリプト言語 Lua を、Luabind を使って組み合わせてみる

みなさん、こんにちは。
kaoruです。

今回は、コンパイル言語 C++ とスクリプト言語 Lua を、Luabind というライブラリーで使って組み合わせて利用する方法を紹介します。

はじめに

まず、C++とLuaを組み合わせようという背景を説明したいと思います。

両者を比較するポイントとして、コンパイル言語かスクリプト言語かという点が重要な要素になります。

コンパイル言語とスクリプト言語の特徴を挙げてみました。

コンパイル言語(C++, JAVAなど)
  • コンパイル時に全ての変数の型の整合性がチェックされるので堅牢
  • 大きなデータを効率的に扱える
  • 実行速度が速い
  • 一度作ってしまえば再利用が容易
スクリプト言語(Lua、Python、PHPなど)
  • 型が動的で柔軟
  • コンパイルやインストール、システム再起動などの前準備せずに動作確認が可能
  • コーディング量が少なく、試行錯誤が簡単
  • 再利用できるように作るには工夫が必要

コンパイル言語とスクリプト言語では、得意分野が大きく違うのです。

そこで、それぞれの得意分野を生かしながら両方を組み合わせて開発をすることでよりよいものを作ることができます。

今回取り上げるLuabindはC++とLuaの間の橋渡しをするためのライブラリーです。

Luaを使ってみよう

読者の中には、Luaを使ったことがない方もいらっしゃると思いますので、雰囲気をご紹介したいと思います。

Hello worldを作ってみよう

Luaはほかのアプリケーションの中に組み込んで使うことが多いのですが、単体でも動作します。

画面にテキストを表示するだけなら1行から書けます。

print "Hello world"

簡単ですね。Luaは行末にセミコロンを書かなくてもOKです。

文とブロック

PHPなどでは、{ と } でブロックを囲みますが、Lua では do と end などで囲みます。

do
    local a = 123
    print a
end
四則演算と式

PHPなどほかの言語に比べると、!= がなくて、かわりに ~= と書かないといけないなど、微妙な違いはありますが、慣れるとあまり大きな違いはありません。

do
    local a, b = 1, 2
    if a ~= b then
        print "Mismatch"
    end
end

さらにメタテーブルという機能を使うと、オブジェクト同士の演算などをカスタマイズできるので、C++のoperatorと感覚が似ていて面白そうです。

関数の定義

関数の定義はJavascriptとよく似ています。ラムダ式も使えます。

function make_less_than(b)
    return function less_than(a)
              return a < b
           end
end

Luabindを使ってみる

簡単に使い方を紹介します。

Luabind / Lua のインクルードファイルをロードします。

#include <luabind/lua_include.hpp>
#include <luabind/luabind.hpp>

まず、Luaステートオブジェクトを作成します。

lua_State* L = lua_open();

lua.hをみると、lua_open()関数はマクロでluaL_newstate()関数に置換されるため、luaL_newstate()関数を直接呼んでもよさそうです。

ここで作成したオブジェクトは、使い終わったら、lua_close(L)関数でクローズしなければいけません。C++では、自分で閉じるのではなく、RAIIクラスやスマートポインターを使って自動的にリソース管理するほうがよいでしょう。

次に標準ライブラリーをロードします。

luaL_openlibs(L);

この関数をロードするためには追加のインクルードファイルが必要かもしれません。

extern "C" {
    #include "lualib.h"
}

Luabindを使う場合は、Luabindの初期化も忘れないように行わなければいけません。忘れるとアプリケーションがクラッシュしてしまいます。

luabind::open(L);
Luabindによる疑似クラス

Luaにはクラスの言語サポートはありませんが、メタテーブルなどの柔軟な言語機能を用いて、クラス相当のものをライブラリーで作ることができます。 今回はLuabindの機能を使ってクラスを作ってみました(yumewaza.cpp, sample.lua)。luabind::open(L)を呼び出すと、class, propertyやsuperなどのキーワードが登録され利用できるようになります。

yumewaza.cpp
#include <string>
#include <sstream>
#include <iostream>
#include <luabind/lua_include.hpp>
#include <luabind/luabind.hpp>


extern "C" {
    #include "lualib.h"
}

int main(int argc, const char *argv[])
{

    if (argc != 2) {
        std::cerr << argv[0] << " <script file.lua>" << std::endl;

        return 1;
    }

    lua_State* L = lua_open();

    luaL_openlibs(L);

    luabind::open(L);

    if (luaL_dofile(L, argv[1])) {

        std::cerr << "ERROR : " << lua_tostring(L, -1) << std::endl;

        lua_close(L);
        return 1;
    }

    lua_close(L);

    return 0;
}

sample.lua
-- 継承元がない独立したクラス MyClass
class 'MyClass'
    -- コンストラクター
    function MyClass:__init()
        self.var_a = 1
    end

    -- 関数1
    function MyClass:func01()
        print("MyClass:func01()")
    end

    -- 関数2
    function MyClass:func02()
        print("MyClass:func02()")
        self.var_a = self.var_a + 1
    end

-- MyClass を継承したクラス MyClass2
class 'MyClass2' (MyClass)
    -- コンストラクター
    function MyClass2:__init()
        MyClass.__init(self)
    end

    -- オーラーライドされた関数2
    function MyClass2:func02()
        MyClass.func02(self)
        print("MyClass2:func02()")
        self.var_a = -self.var_a
    end

do
    print "---------- test01 ---------"
    local obj01 = MyClass()
    print(string.format("self.var_a = %d", obj01.var_a))
    obj01:func01()
    print(string.format("self.var_a = %d", obj01.var_a))
    obj01:func02()
    print(string.format("self.var_a = %d", obj01.var_a))

    print "---------- test02 ---------"
    local obj02 = MyClass2()
    print(string.format("self.var_a = %d", obj02.var_a))
    obj02:func01()
    print(string.format("self.var_a = %d", obj02.var_a))
    obj02:func02()
    print(string.format("self.var_a = %d", obj02.var_a))
end
sample.luaの実行例
---------- test01 ---------
self.var_a = 1
MyClass:func01()
self.var_a = 1
MyClass:func02()
self.var_a = 2
---------- test02 ---------
self.var_a = 1
MyClass:func01()
self.var_a = 1
MyClass:func02()
MyClass2:func02()
self.var_a = -2

サンプルに示すように、classキーワードを使うことで、本当のクラスのように定義が書けるほか、継承や、メソッドのオーバーライドも可能になります。 自分で独自にクラス機構を実装することもできますが、Luabindを使っている場合は、ぜひ利用してみてはどうでしょうか。

C++のクラスや構造体をLuaで使う

C++のクラスや構造体をLua側で使えるように登録するには次のようにします。

例として次の構造体がC++側であるとします。

struct my_struct_t
{
    int var_a;
    std::string var_b;

    boost::optional<std::string> var_c;

    void method_a();

    int method_b();
    std::string method_c();
};

これをLua側で使えるようにLuabindを使って登録してみます。

luabind::module(L)
[
    luabind::class_<my_struct_t>("my_struct_t")
        .def_readwrite("var_a", &my_struct_t::var_a)
        .def_readwrite("var_b", &my_struct_t::var_b)
        .def_readwrite("var_c", &my_struct_t::var_c)
        .def("method_a", &my_struct_t::method_a)
        .def("method_b", &my_struct_t::method_b)
        .def("method_c", &my_struct_t::method_c)
];

C++のフリー関数をLuaで使う

あるいは、C++のフリー関数をLua側で使えるように登録するには次のようにします。

C++側で、次のようなフリー関数があったとすると

my_struct_t create_my_object()
{
    return my_struct_t();
}

Lua側で使えるようにするには、次のようにします。

luabind::module(L)
[
    luabind::def("create_my_object", &create_my_object)
];

C++からLuaスクリプトを実行

このような準備をした後、luaL_dostring関数を使えば、Luaのスクリプトを文字列で渡して実行できます。

    luaL_dostring(lua_state.get(),
        "function set_a(obj, value) \n"
        "    obj.var_a = value      \n"
        "end                        \n"
    );

C++からLuaスクリプトファイルをロードして実行する

あるいは、Luaスクリプトをファイルからロードするには次のようにします。

if (luaL_dofile(L, "script.lua")) {
    std::cerr << "ERROR : " << lua_tostring(L, -1) << std::endl;

    BOOST_ASSERT( false );
}

Luaの関数をC++から呼び出す

また、Luaスクリプトの中で定義されている関数をC++側から直接呼び出すこともできます。

my_struct_t obj;
luabind::call_function<void>(
    L,
    "set_a",

    boost::ref(obj),
    123
);

呼び出されるLua側のソースコードは例えば次のようになります。

function set_a(obj, value)
    obj.var_a = value
end

Lua側でC++側の変数の値を変更するには

ここで、objをそのまま渡さずに、boost::ref(obj)としているのは、objをそのまま渡してしまうと、objのコピーが作成されて、それがLua側に渡されるために、Lua側でオブジェクトのプロパティーを書き換えても、呼び出し元のobj変数は変更されないからです。

boost::refを利用すると、参照としてLua側にオブジェクトを引き渡せるので、Lua側での変更が、きちんと、C++側に反映されます。

Luaの文字列/nilをboost::optionalを使って扱う

また、数値や文字列だけではなく、nilをC++とLuaでやりとりしたい場合もあると思います。

今回は、boost::optionalを使ってC++側でnilを表現することにしました。それには、LuaとC++の双方で型をコンバートする方法をLuabindに教える必要があります。

#include <string>
#include <boost/optional/optional.hpp>
#include <luabind/lua_include.hpp>
#include <luabind/luabind.hpp>

namespace luabind

{
    template <>
    struct default_converter<boost::optional<std::string>>

      : native_converter_base<boost::optional<std::string>>
    {
        static int compute_score(lua_State* L, int index)
        {

            using namespace std;

            switch (lua_type(L, index))
            {

                case LUA_TNIL:
                case LUA_TSTRING:
                    return 0;

                default:
                    return -1;
            }
        }

        boost::optional<std::string> from(lua_State* L, int index)
        {

            using namespace std;

            switch (lua_type(L, index))
            {

                case LUA_TNIL:
                    return boost::optional<std::string>();

                case LUA_TSTRING:
                default:
                    return string(lua_tostring(L, index));
            }
        }

        void to(lua_State* L, boost::optional<std::string> const& x)
        {

            using namespace std;

            if (x)
                lua_pushstring(L, x->c_str());

            else
                lua_pushnil(L);
        }
    };

    template <>
    struct default_converter<boost::optional<std::string> const&>

      : default_converter<boost::optional<std::string>>
    {};
}

今回は、boost::optionalとLuaの文字列/nilを相互に変換していますが、C++の他の型をLuaとやりとりする方法を定義する参考になればと思います。

Luaスクリプト中で発生したエラー情報の取得

デフォルトではLuaスクリプトにエラーがある場合、あまり情報を取得することができませんが、luabind::set_pcall_callback というメソッドでエラーハンドラーを指定することができます。

今回、サンプルを作ってみました。

int lua_error_handler(lua_State* L)
{
    lua_Debug d = {};
    std::stringstream msg;

    // スタックからエラーメッセージを取得する
    std::string err = lua_tostring(L, -1);

    msg << "ERROR: " << err << "\n\nBacktrace:\n";

    for (int stack_depth = 1; lua_getstack(L, stack_depth, &d); ++stack_depth) {

        lua_getinfo(L, "Sln", &d);

        msg << "#" << stack_depth << " ";

        if (d.name)
            msg << "<" << d.namewhat << "> \"" << d.name << "\"";

        else
            msg << "--";

        msg << " (called";

        if (d.currentline > 0)
            msg << " at line " << d.currentline;

        msg << " in ";
        if (d.linedefined > 0)

            msg << "function block between line " << d.linedefined << ".." << d.lastlinedefined << " of ";

        msg << d.short_src;
        msg << ")\n";
    }

    // スタックに積まれているエラーメッセージを、新しい文字列に置換する。
    lua_pop(L, 1);
    lua_pushstring(L, msg.str().c_str());

    std::cout << msg.str() << std::endl;

    return 1;
}

luabind::set_pcall_callback(lua_error_handler);

実行例を、以下に示します。

ERROR: [string "function _set_a(obj, value) ..."]:2: attempt to index field 'var_a' (a number value)

Backtrace:
#1 <global> "_set_a" (called at line 2 in function block between line 1..3 of [string "function _set_a(obj, value) ..."])
#2 -- (called at line 5 in function block between line 4..6 of [string "function _set_a(obj, value) ..."])

1行目にエラーの内容(この例では、var_aは数値であるのに、インデックスフィールドが参照された旨が示されている。)が表示され、 Backtrace:の次の行から、呼び出し履歴が表示されています。#1によると、エラーの個所はグローバル関数の_set_aの中の2行目(それはLuaスクリプトとして渡された文字列の1?3行目で定義されている。)から呼び出されていることが分かり、#2によると、それは、5行目から呼び出されていることがわかります。

このようにエラーハンドラーを設定することで、エラーが発生した場所や、スクリプトのファイル名、関数の呼び出し履歴などを取得し、表示することができます。

Luabind がVS2010でコンパイルエラーになる場合には・・・

余談ですが、筆者の環境ではVisual Studio 2010でLuabindがコンパイルエラーになったため、Luabindのソースコードを若干変更しました。

具体的には、std::pairのコンストラクターを呼び出している個所で、C++0xに関連して、0の扱いがあいまいになってしまいコンパイルエラーになっていたので、明示的なキャストをして問題を解決しました。

diff --git a/luabind/lua_include.hpp b/luabind/lua_include.hpp
index 899df14..368b61a 100755

--- a/luabind/lua_include.hpp
+++ b/luabind/lua_include.hpp
@@ -25,8 +25,8 @@

 extern "C"
 {
-       #include "lua.h"
-       #include "lauxlib.h"

+       #include <lua.h>
+       #include <lauxlib.h>
 }

 #endif
diff --git a/src/inheritance.cpp b/src/inheritance.cpp
index 45daeb8..4c7293b 100644

--- a/src/inheritance.cpp
+++ b/src/inheritance.cpp
@@ -154,7 +154,7 @@ std::pair<void*, int> cast_graph::impl::cast(

     if (cached.first != cache::unknown)
     {
         if (cached.first == cache::invalid)
-            return std::pair<void*, int>(0, -1);
+            return std::pair<void*, int>(static_cast<void *>(0), -1);

         return std::make_pair((char*)p + cached.first, cached.second);
     }

@@ -192,7 +192,7 @@ std::pair<void*, int> cast_graph::impl::cast(

     m_cache.put(src, target, dynamic_id, object_offset, cache::invalid, -1);

-    return std::pair<void*, int>(0, -1);
+    return std::pair<void*, int>(static_cast<void *>(0), -1);
 }

 void cast_graph::impl::insert(

おわりに

駆け足で、紹介してきましたが、C++とLua、Luabindを利用した開発のイメージはつかめましたでしょうか。コンパイル言語での開発の中にスクリプト言語を取り入れると、自由度が上がり、開発の幅が広がりますので、ご興味のある方は一度試してみられるといいと思います。

LuaのAPIをC言語から呼び出す際には、Luaスタックを介してやりとりをしなければいけないので、 少々面倒くさい面があるのですが、C++からLuabindを利用すると、Luaスタックの操作はほとんど ライブラリー側で面倒をみてくれるので、本当にやりたいことだけを書けばよくなっています。

まだまだ、書ききれなかったことはたくさんありますが、LuaやLuabindのオフィシャルサイトの他、最近は、ユーザーのブログや書籍もたくさんありますので、困った時にはいろいろと探してみるといいかもしれません。

最後に、オフィシャルサイトのリンクを掲載しておきます。

http://www.lua.org/
Luaのサイト 最新のLuaがダウンロードできます

http://www.rasterbar.com/products/luabind.html
Luabindのサイト 最新のLuabindがダウンロードできるほか、ドキュメントがとても参考になります。

タグ: | | | | |
日時: 2010年06月25日 16:22 | コメント (0) | トラックバック (0)

PhotoShop・Illustrator CS5 からWeb用に保存したファイルが一部携帯端末で見れない場合

はてなブックマーク -PhotoShop・Illustrator CS5 からWeb用に保存したファイルが一部携帯端末で見れない場合

こんにちは。Nairoです。

CS5が発売されましたね。
使っている方も多いかと思いますが、先日CS5で書き出したファイルが初期設定のままでは、一部携帯端末(例えばSoftbank 905SH)で表示しないということに気づきました。
初期設定ではWeb書き出し機能(Web およびデバイス用に保存)にメタ情報を付与するようになっています。
これです。

メタデータ

このまま初期設定で保存してしまうとメタ情報などが付与されてしまい、一部端末で表示しないことがあります。
ということでメタ情報をなしに。
情報を付けなければファイル容量も小さくなりますし忘れずに!
特にインストールした直後などは注意しましょう。

メモ書きでした。

日時: 2010年06月08日 20:29 | コメント (0) | トラックバック (0)

MySQL "SHOW ENGINE INNODB STATUS"の読み方 その1

はてなブックマーク -MySQL

kouです。


何か困ったときに便利なのが各種統計情報出力コマンド。弊社では堅く作るシステムではMySQLを利用することが多く、ストレージエンジンもInnoDBを利用させていただくことが多いです。

というわけで負荷テスト時や運用時に"SHOW ENGINE INNODB STATUS"コマンドをよく使うのですが、各参考サイトを元に自分のメモ代わりに必要そうな項目をまとめてみました。


以下、各セクション毎にポイントをまとめていこうと思います。間違い等があればご指摘いただけるとうれしいです。


出力サンプルのMySQLのバージョンは5.0系です。

ステータスセクション

1  mysql> SHOW ENGINE INNODB STATUS\G
2  *************************** 1. row ***************************
3  Status:
4  =====================================
5  100530  9:10:18 INNODB MONITOR OUTPUT
6  =====================================
7  Per second averages calculated from the last 26 seconds


7行目のPer second ...は統計のためにデータを取得した時間です。とはいえ、全てのセクションの情報が同時に採取されるわけではないらしいので、この例では以降のセクションのカウンタ値はだいたい26秒間のもの。秒平均値は26秒間の中のどこかの期間内の平均値であることがわかります。


セマフォセクション

 8 ----------
 9 SEMAPHORES
10 ----------
11 OS WAIT ARRAY INFO: reservation count 359260, signal count 356251
12 Mutex spin waits 0, rounds 6471518, OS waits 83492
13 RW-shared spins 438760, OS waits 214212; RW-excl spins 34083, OS waits 15339


リレーショナルデータベースではそのデータの整合性を担保するために様々な処理でアトミック性を保証しなくてはいけません。MySQLで複数スレッドが動いている中で、クリティカルな処理を行う場合には、そのアトミック性を担保するために同時に1つの操作しかしていないことを保証する仕組み(同期機構)が必要です。LinuxのMySQLでは同期機構にセマフォとMutexを利用しています。

このセクションではそのセマフォ、Mutexに関する統計情報が出力されています。

11行目はスレッドがセマフォの配列に入ったOS待ち(OS wait)の回数です。reservation countが配列に入った回数、signal countが配列に入ってOSからシグナルを受け取った回数です。後述しますが、この回数は少ないほど良いです。

12行目はスレッドのMutex(相互排他ロック)による各種待ち回数を示していて、spin waitsはMutexのスピンロック待ちに入った回数、roundsはスピンロック待ちに入ったロックのスピンラウンド総数を示します。OS waitsはスピンロック待ちでもMutexを取得できなかったため、オペレーティングシステム管理下のセマフォに渡されたロック回数です。

InnoDBはスピンロック待ち→OS待ちの順にロック取得を試みます。スピンロック待ちに入ったロックはinnodb_sync_spin_loopsで設定されたスピンラウンド数(取得待ち回数)を超えるとOS待ちに引き渡されます。OS待ちはスピン待ちに比べて処理コストが大きいので一般的にはOS待ち数が小さい方が好ましいです。OS待ちが多く発生している場合はディスクI/OかInnoDB内部でこれらロックに関する競合が発生している可能性が高いです。OS待ちが毎秒10000以上であればOS待ちを減らす方向でチューニングしましょう。MySQLのスレッド数の変更(innodb_thread_concurrency設定値)が効くと思います。

とはいえ、スピン待ちもCPUリソースを消費するため、あまりspin rounds数が大きいようだとスピン待ちとOS待ちのバランスを取るためにチューニングが必要です(前述のinnodb_sync_spin_loops変数で設定できます。まだ試したことないですが、spin roundが毎秒1000以上であればinnodb_sync_spin_loopsを変更すると良いそうです。もしくはinnodb_thread_concurrency設定値を減らす)。

13行目は読み書きに関する共有ロック(RW-shared)、排他ロック(RW-excl)の各待ち種別カウンタを示していて、それぞれスピン待ち(spins)、OS待ち(OS waits)の回数を示しています。

まとめると、データベースへの並列アクセスが多い場合は同期機構に密接に関わるこのセクションの数字が特に重要で、ほとんどの状況ではOS待ちが多く発生してパフォーマンスが劣化していると思います。その場合はディスクI/OやInnoDB内部の競合が発生している可能性が多いので、MySQLのスレッド数を減らす(innodb_thread_concurrency)と症状が改善されることが多いです。パフォーマンスが出ないからとスレッド数をむやみに増やすと逆に競合が発生しやすくなるため逆効果です。


(もちろん、ボトルネックがどこにあるかによって増やした方がいい場合もありますので、innodb_thread_concurrencyを変更しながらベンチマークをとってチューニングすることが一番望ましいです。)


「その2」へ続く。

参考サイト/書籍:
MySQL Performance Blog - SHOW INNODB STATUS walk through
MySQL 5.0 Reference Manual - 13.2.13.2. SHOW ENGINE INNODB STATUS and the InnoDB Monitors
実践ハイパフォーマンスMySQL 第2版

タグ: | |
日時: 2010年06月01日 01:37 | コメント (0) | トラックバック (0)

Autoconf/configureスクリプトを使ってアプリケーションの設定を自動化

はてなブックマーク -Autoconf/configureスクリプトを使ってアプリケーションの設定を自動化
みなさん、こんにちは、
kaoruです。

はじめに
UNIX環境でアプリケーションをインストールしたことがある方には、次のコマンドはおなじみだと思います。
./configure
make
make install

これらのコマンドは通常はC/C++で書かれたアプリケーションで利用されることが多いのですが、PHPやPythonなど、スクリプト言語で書かれたアプリケーションであっても利用価値があります。 とはいえ、スクリプト言語の開発者にはあまり馴染みがないと思いますので、簡単に紹介をしたいと思います。

Autoconf/configureスクリプトを利用するメリット

よくある設定ファイルを直接書き換えるような方法だと、書き換えた設定ファイルをバージョン管理システムに登録できないので、困ってしまいます。環境ごとに設定ファイルを複数用意して、全部バージョン管理するような方法だと、一箇所を修正すると全部のファイルを忘れずに修正しないといけなくなります。

また、設定ファイルに誤りがあっても、すぐにはわからないという問題があります。configureスクリプトはシェルスクリプトですから、どんな方法のエラーチェックも可能です。

configureスクリプトとは

configureスクリプトの役割はテンプレートとなるテキストファイルを入力として、文字列置換を行い、出力となるファイルを書き出すことです。よくある入力ファイルはMakefile.inで、出力ファイルはMakefileなのですが、ここでは、config.iniというスクリプト言語用の設定ファイルを出力することを考えてみましょう。

テンプレートファイルの書き方

出力したいファイルのファイル名の末尾に「.in」をつけたファイル名を用意します。 置き換えたい場所に、@変数名@というプレースホルダーの記述をします。ここでいう変数名はシェルの変数名で、configureスクリプトから渡されます。

configure.acの書き方

こちらは、autoconfのマニュアルを見てほしいのですが、ポイントとしてはAC_SUBSTというマクロを使うことで、先ほどのテンプレートファイルで指定したプレースホルダーの記述を置換することができます。

configureスクリプトの生成方法
シェルのプロンプトから
autoreconf
と入力するだけで、configure.acからconfigureスクリプトが生成されます。
configureスクリプトの使い方

./configure --help とすると、おなじみのヘルプが表示されます。

サンプル
config/config.ini.in
BASE_DIR = @abs_srcdir@
MASTER_DB = @MASTER_DB@
SLAVE_DB = @SLAVE_DB@
IMAGE_DIR = @IMAGE_DIR@

configure.ac
AC_PREREQ(2.59)
AC_INIT(MyProject, 1.00)

AC_CONFIG_SRCDIR([config/config.ini.in])

AC_ARG_ENABLE(
    [image-dir],
    AS_HELP_STRING([--enable-image-dir=ARG], [画像ファイルの置き場所]),
    [IMAGE_DIR=$enableval],
    [IMAGE_DIR=$prefix/htdocs/image]
)
AC_SUBST(IMAGE_DIR)

AC_ARG_ENABLE(
    [master-db],
    AS_HELP_STRING([--enable-master-db=ARG], [マスターDBのDSN]),
    [MASTER_DB=$enableval],
    [MASTER_DB=]
)
AC_SUBST(MASTER_DB)

AC_ARG_ENABLE(
    [slave-db],
    AS_HELP_STRING([--enable-slave-db=ARG], [スレイブDBのDSN]),
    [SLAVE_DB=$enableval],
    [SLAVE_DB=]
)
AC_SUBST(SLAVE_DB)

AC_CONFIG_FILES([config/config.ini])
AC_OUTPUT
サンプルの実行例
autoreconf
./configure --enable-image-dir=/home/test/image --enable-master-db="mysql:host=master-db01.sample port=3306 user=web_user" --enable-slave-db="mysql:slave-db01.sample port=3306 user=web_user"

次のようなメッセージが表示されます。

configure: creating ./config.status
config.status: creating config/config.ini

config/config.ini が生成されているはずなので、確認してみてください。

日時: 2010年05月31日 19:59 | コメント (0) | トラックバック (0)

TokyoTyrantとMySQLはSSDによって性能が上がるのか?

はてなブックマーク -TokyoTyrantとMySQLはSSDによって性能が上がるのか?

お久しぶりです。kouです。


先日、社内勉強会を実施しました!

今回は「Tokyo TyrantとMySQLはSSDによって性能があがるのか?」をテーマにちきんさんが社内講師を務めました。現在、ゆめみではソーシャルアプリ開発を進めていて、そこで利用するTTやSSDの技術検証を行った結果をみんなで共有です。


勉強会の詳細はスライドをご覧ください。




以下、勉強会の様子。


study_201004_01.jpg

study_201004_02.jpg


次回はUstreamで中継してみたいです。

日時: 2010年04月23日 18:10 | コメント (0) | トラックバック (0)

C++/Boostとコルーチンを使って非同期I/Oを実現(C10K問題もこれで解決?)

はてなブックマーク -C++/Boostとコルーチンを使って非同期I/Oを実現(C10K問題もこれで解決?)

みなさん、こんにちは、
kaoruです。

今回は、C10K問題 (参考1, 参考2) を回避するためにコルーチンを使う方法をご紹介したいと思います。

また、ちょっと実用的な例として、言語としてC++、ライブラリーとしてBoost.AsioとBoost.Asio付属のコルーチンを利用して、 ApacheBenchもどきを実際に作ってみました。

ネットワークプログラミングを調査しようと思ったきっかけは、 技術評論社から出ている「WEB+DB PRESS Vol.55」の 「モダンネットワークプログラミング入門」という記事です。 こちらには、ネットワークプログラミングの基本的な設計モデルが分かりやすく説明されているため、とても参考になります。

ご存じのように、C10K問題とは、ハードウェアに余裕があるにもかかわらず、同時コネクション数が1万前後になると、パフォーマンスが急速に劣化するという問題です。 これは、ネットワークアプリケーションの伝統的な設計ではコネクション数に比例する数のプロセスやスレッドが生成されることに原因があります。 例として、Apache HTTP Serverのpreforkモードでは、コネクション数に比例する数の子プロセスが起動して、リクエストを処理します。 同時コネクション数が1万前後にするには、子プロセスを1万前後起動しなければならず、これはあまり現実的な数字ではありません。

C10K問題を回避するためには、コネクション数とプロセス数やスレッド数が比例しないような設計をしなければなりません。
これには、非同期I/Oを利用したイベント駆動モデルが有効です。 これはすなわち、接続成功、ヘッダーの読み取り完了、レスポンスの送出完了などといったイベントの発生毎にあらかじめ登録しておいたイベントハンドラを呼び出してもらう方法です。 この方法は非常に効率がよいのですが、本来はひと続きの処理がたくさんのイベントハンドラの中にばらばらになって散らばってしまうため、 処理の流れが分かりにくくソースコードの可読性が悪いという欠点がありました。

コルーチンと呼ばれる手法を使うと、非同期I/Oを利用したイベント駆動モデルでありながら、同期モデルとほぼ同じようにプログラミングを行うことができ、 処理の流れが非常に分かりやすくなります。
コルーチンとは、プログラムの流れの中で中断・再開ができたり、スレッドのように制御を分岐したりできるプログラミングのしくみです。Luaなどの一部の言語では言語レベルでサポートされていますが、C/C++ではライブラリーを使って実装します。

今回作ったソースコードの一部を掲載します。 (この記事の下のリンクから全てのソースコードをダウンロードできます。)


#include "yield.hpp" // Enable the pseudo-keywords reenter, yield and fork.

void client::operator()(

        boost::system::error_code ec,
        std::size_t length,
        tcp::resolver::iterator endpoint_iterator)
{

    static int n = 0;
    static int c = 0;

    if (ec && ec != boost::asio::error::eof) {

        std::cerr << ec.message() << std::endl;
        return;
    }

    // On reentering a coroutine, control jumps to the location of the last
    // yield or fork. The argument to the "reenter" pseudo-keyword can be a
    // pointer or reference to an object of type coroutine.
    reenter (this)
    {
        for (c = 0; c < concurrency_level_ && c < num_request_; ++c) {

            fork client(*this)();

            if (is_child())
                break;
        }

        if (is_parent())
            return;

        while (n < num_request_) {

            socket_.reset(new boost::asio::ip::tcp::socket(io_service_));

            yield socket_->async_connect(endpoint_, *this);

            n++;
            if (n % 1000 == 0)

                cout << "Completed " << n << " requests" << endl;

            // Send the request.
            yield boost::asio::async_write(
                *socket_,
                boost::asio::buffer(

                    request_.c_str(),
                    request_.size()
                ),
                *this
            );

            // Start reading remaining data until EOF.
            static char dummy_buffer[1 * 1024 * 1024]; // Dirty buffer.

            yield boost::asio::async_read(
                *socket_,
                boost::asio::buffer(dummy_buffer, sizeof(dummy_buffer)),

                boost::asio::transfer_all(),
                *this);

            // Initiate graceful connection closure.
            socket_->shutdown(tcp::socket::shutdown_both, ec);
        }
    }
}

#include "unyield.hpp" // Disable the pseudo-keywords reenter, yield and fork.

#include "yield.hpp" から #include "unyield.hpp" の区間では、 reenter, yield, fork という3種類のキーワードが有効になります。
(実際にはプリプロセッサーマクロですが、キーワードであるかのように使えます。)

コルーチン化したいコードの部分をreenter { } で囲み、 async_connect, async_read, async_write といった非同期I/Oを行う関数の手前に、 yieldキーワードをつけます。(下図の(1),(2))
forkキーワードは、UNIXのforkシステムコールと同じような感じに、コルーチンオブジェクトをコピーして子供のコルーチンを生成します。(下図の親・子) is_child(), is_parent()という関数で、子として実行しているのか、親として実行しているのかがわかりますので、 処理を分岐することが可能です。forkキーワードを使っても、実際にはプロセスやスレッドは生成されませんので、非常に軽量です。

yieldキーワードを使うと、非同期I/Oが完了したときに次の行から処理が再開されます。すなわち、下図で説明すると、まず最初に(1)から(2)まで処理が進み、ここでyieldキーワードがありますので、async_connect関数(非同期接続を行う関数です。)が実行された後、一旦reenterブロックを抜けます。抜ける際に、再開する場所として(2)の場所が自動的に保存されます。(2)のasync_connectの引数として*thisが渡されていますので、接続完了時にもう一度(1)に制御が来ます。ここで、先ほど(2)まで処理が進んだことを保存していましたから、(1)から(2)に制御が移動し、(2)から処理が再開されるという仕組みです。

co-routine.gif

以前はこのような処理を記述する際には、あらかじめ同時接続数と同じ数のスレッドを生成した後、 同期処理を行うAPIを用いて記述することが多かったのですが、今回はスレッドは明示的に生成することなく実装ができました。

この例のように、まるで同期処理を書いているかのような感覚でプログラミングを行うことができるのがうれしいところです。

前回の記事「WEBサーバーを比較してみる」と同じ環境で、nginxサーバーに対してリクエストを発行したところ、 ApacheBench を若干上回る 9345.84 req/s という結果を得ました。

また、機能が違うため正しい比較はできませんが、同時コネクション数を10000程度まで増やしても、
ApacheBenchよりもメモリー使用量が少なくなりました。

今回、コルーチンを使うことで、ApacheBenchもどきのクライアントを非常に簡単に作ることができました。
みなさんが、非同期I/Oを使ったプログラミングをされる際に、少しでも参考になるところがありましたら幸いでございます。

今回作成したソースコード
ファイルをダウンロード

ビルド方法
※ビルドには、あらかじめBoost C++ Librariesをインストールして、適切にパスの設定をする必要があります。

Windows環境の場合
Visual Studio 2008 で、http_stress_tool.slnを開いてください。

UNIX環境の場合
Makefile等は用意していません。以下のようなコマンドでビルドができます。
g++ -O3 -o http_stress_tool http_client.cpp main.cpp -Wall -Wextra -lboost_system -lboost_date_time


日時: 2010年04月19日 16:00 | コメント (0) | トラックバック (0)

WEBサーバーを比較してみる

はてなブックマーク -WEBサーバーを比較してみる

はじめまして、kaoruです。

ゆめみでは伝統的にWEBサービスを提供するにあたって、Apache HTTP Server+PHPというミドルウェア構成を利用してきました。

近年では単純な閲覧型のWEBサイトを越えて、SNSやコミュニティーサービスなど、ユーザーが自らリアルタイムに参加できるようなWEBサービスが一般的になってきています。このようなWEBサービスを提供するためには、膨大なトラフィックをリアルタイムに処理できるようなシステム構成が求められています。

 このような背景もあり、今回は今までよく使っていたApache HTTP Serverと最近よく目にするようになってきたnginxというWEBサーバーの基本的な性能を比較してみましたのでご紹介してみたいと思います。

なお、今回の性能の計測は簡易的なものであり、参考程度のものであることはご了承ください。

20100331.WEB-server-compara.gif

グラフの縦軸は1秒間あたりに処理したリクエスト数の平均、横軸は同時接続数で、localhostからApacheBenchを使って計測しました。上の3つはApache HTTP Serverで、configure時に指定するMPMオプションを、prefork, worker, eventと変えたものです。nginxは最近よく聞くようになったWEBサーバーです。Boost.AsioはC++のライブラリーで、非同期のネットワーク処理ができます。ライブラリーに付属しているサンプルプログラムを試してみました。

結果を見ると、Apache HTTP Serverよりもnginxはコネクション数にかかわらず性能が高いことが分かります。また、グラフからはわかりませんが、アーキテクチャーの違いからか、nginxの方がリソースの消費量が大幅に少なくなっているようです。Boost.Asioはライブラリーなので他のアプリケーションと単純な比較はできませんが、アプリケーションを実装するプラットフォームとしてはかなり高性能であると言えます。

今回は、手始めとして、固定の簡単なテキストファイルを取得する試験を行いました。動的にコンテンツを返さなければいけないかどうかや、使いたい機能によってもかわってきますが、nginxで必要なことができるなら、とても魅力的な選択肢になると思います。

今回の試験に用いたサーバーのスペックとアプリケーションのバージョンを挙げておきます。

Intel(R) Xeon(R) 2.33GHz (64bit, 8コア)
Linux 2.6.18 x86_64
Mem:   1048576k total

httpd-2.2.14
nginx-0.8.34
boost_1_42_0
日時: 2010年04月01日 17:25 | コメント (0) | トラックバック (0)

[mod_ktai] FAQ その1

はてなブックマーク -[mod_ktai] FAQ その1

こんにちは、kouです。

日頃、mod_ktaiをご利用いただきありがとうございます!


mod_ktaiの利用法に関してご質問をいただくことがあるのですが、全てのご質問にお答えできておらず大変申し訳なく思っています。今後はmod_ktaiに関していただいたご質問についてここにまとめていければと思ってますので、ご参考になれば幸いです。


Q. Apacheを起動しようとしたところ下記のようなエラーが発生します。



Cannot load /etc/httpd/modules/mod_ktai_image.so into server:
/etc/httpd/modules/mod_ktai_image.so:
undefined symbol: _ZN7libktai4info8constant29X_KTAI_INFO_EXT_SCREEN_HEIGHTE


A. モジュールの読み込み順に原因があります。

mod_ktaiシリーズはmod_ktai_infoが存在することを前提とした各モジュールの作りになっています。とくにmod_ktai_imageはmod_ktai_infoへの依存度が高いためmod_ktai_imageがApacheに読み込まれる前にmod_ktai_infoが読み込まれていないと上記のようなエラーが発生してしまいます。必ず

  1. mod_ktai_info
  2. mod_ktai_image

の順に読み込まれるようにLoadModuleの定義をしてください。


Includeを利用して外部ファイルから定義を読み込んでいる場合

Include conf.d/ktai_info.conf
Include conf.d/ktai_image.conf

httpd.confにLoadModuleを直接記述している場合

LoadModule ktai_info_module modules/mod_ktai_info.so
LoadModule ktai_image_module modules/mod_ktai_image.so
タグ: |
日時: 2010年02月24日 11:05 | コメント (0) | トラックバック (0)
ゆめみ深田浩嗣のブログ
YUMEMI Labs Sweet