トップ 最新 追記

Public Diary


2006-10-05

[ネット] Google Code Search 発表 (CNET)

この記事を見て、「お、Googleもついにオープンソースになったか!」と期待してしまった…。

Googleは米国時間10月5日、新サービス「Google Code Search」を発表した。プログラマーはこのサービスを利用して膨大な数のソースコードを検索できるという。

[CNET Japan, 10月5日より引用]

でも、読んでみると単にすでに公開されてるGNUなどのソースを検索できるってだけみたい。"Google Code"のSearchじゃなくて、Google's "Code Search" なのね。まぁ、さすがに核心ともなるPage Rankの部分だとかは表に出さないんだろうか。でも、もはや圧倒的なページキャッシュ量とインフラを誇るGoogleなら、検索エンジンのソースコードを公開したところで追従できる者はいないだろうに…


…いや、いる。マイクロソフトだ。

マイクロソフトはオープンソース陣営と真っ正面から敵対しているわりには、オープンソースのソフトウェアからどんどんコードやアイデアを取り込んでいる。ずるいなぁ。

(追記)

で、早速 Google Code Searchを使ってみたのだけど、

いまいち純粋なglibcで書かれているようなものがなくて、しょんぼり (ラッパやら構造体とかを新たに定義しなきゃいけない)。結局、directory_exists→directory_existと単複を読み替えて調べると出てくる samba のソースを使うことになるんだろう。他にも、

これを読んで、「あぁ、C言語でflockでファイルにロックをかけたい時はこんなコード(↓)にすればいいんだな」と思うレベルの人ははじめからCode Search使わなくてもman flockとかでなんとかなるんじゃないか。

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int fd;
extern char* filename;
  :
fd = open(filename, O_RDWR | O_CREAT);
flock(fd, LOCK_EX);
  :

まぁそんなわけで、なんだかそのまま使えるコードも少なくて、ソースハックとかの興味レベルを超えられないような気がする。(ちなみに、どちらも今日仕事で作ったプログラムの一部をふと思い出して検索してみた)

…とだけ書くのも何なので、具体的なロックのソースも一応掲出しておきます。


2006-10-06

[プログラミング] C++でロック(flock, lockf, fcntl)

昨日の日記でロックのことを書いたら、その後Googleでflockなどを調べてここに辿り着く人もいるようなので、一応ソースも掲出。

C++ではファイルの入出力を主にfstreamで行うことが想定されているものの、fstreamとそのバッファクラスのfilebufにはファイルのロック機構は用意されていません。

flock などの関数はC言語では比較的低レベルな関数でシステムコールに近く、純粋な(理想的な)言語体系を求めたC++にはそぐわなかったためと思われます。ただ、filebufからも同様の理由でファイルデスクリプタやFILE構造体のポインタを取得することができず、これらを引数とするflockを行うことができません。

といった事情から、C++でロック機構を行うためには、

  • 独自にfilebufを拡張する
  • ロックファイルを別途用意する

といった処理が必要となります。

工程数やデバッグのしやすさから言うと後者のほうがはるかに楽なので、仕事上は後者の方法を使っています。一応ソースだけ出しておきます:

lockfile.cpp

/* Traditional C headers */
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>

/* C++ header */
#include <string>  // for filename member

/* My header */
#include "lockfile.hpp"

namespace fileutil {

void lockfile::reset()
{
  if (fd) {
    close(fd);
    fd = 0;
  }

  m_flock.l_type = F_WRLCK;
  m_flock.l_start = 0;
  m_flock.l_whence = SEEK_SET;
  m_flock.l_len = 0;
  m_flock.l_pid = getpid();
}

bool lockfile::lock()
{
  if (fd != 0)
    return false;
  if (filename.length() == 0)
    return false;

  fd = open(filename.c_str(), O_CREAT|O_WRONLY|O_NONBLOCK, 0644);
  if (fd < 0)
    return false;
  return (fcntl(fd, F_SETLKW, &m_flock) != -1);
}

bool lockfile::unlock()
{
  if (fd == 0)
    return false;
  close(fd);
  unlink(filename.c_str());
  reset();

  return true;
}

}  // endof namespace fileutil

lockfile.hpp

#ifndef LOCKFILE_HPP
#define LOCKFILE_HPP

/* Traditional C headers */
#include <fcntl.h>

/* C++ header */
#include <string>

namespace fileutil {

/* class definition */
class lockfile
{
  public:
    bool lock();
    bool unlock();
    lockfile() { reset(); }
    lockfile(std::string fname) { reset(); filename = fname; }
    ~lockfile() { if (fd != 0) unlock(); }
    std::string filename;
  private:
    int fd;
    struct flock m_flock;
    void reset();
};

}  // namespace fileutil

#endif /* LOCKFILE_HPP */

こんな風に使います:

#include <iostream>
#include "lockfile.hpp"

int main(int argc, char** argv)
{
  fileutil::lockfile lck("/mnt/some_nfsdev/lockfile");

  std::cout << "Waiting for lock..." << std::endl;
  lck.lock();
  std::cout << "Lock is set." << std::endl;


  /* DO SOMETHING */
  sleep(5);


  lck.unlock();
  std::cout << "Lock is unset." << std::endl;
}

本当は、ロックファイルがすでにある場合にロックするかどうかを判断させるような仕組みがあったほうがよいのかもしれませんが、とりあえず自分で使う分にはこれで十分だったので。一応。

(追記)

さて、fstream側を拡張させる方法についても簡単に。というわけでGCCのソースを引っ張り出す。

GCCでは、移植性を保つために、システムコールに近い部分になればなるほど、幾重にもラッパ関数やらテンプレートやらで隠蔽されてて、複数のクラスが出てきてちょっとイライラします。

で、とりあえず出力ストリームにロックをかけるには何らかの形で ファイルデスクリプタを取り出す必要があるので、そういった観点でソースを紐解いて今のところわかったこと:

/usr/include/c++/3.3/fstream:

  • ofstream のテンプレートクラスは basic_ofstream。
  • basic_ofstream のプライベートメンバ変数に _M_filebuf (型は basic_filebuf<>) があって、パブリックメソッドの rdbuf() でこいつのポインタを呼び出せる
  • basic_filebuf は、プロテクテッドな __basic_file クラスのメンバ変数 _M_file を持つ (フレンドクラスは ios_base)。

./libstdc++-v3/config/io/basic_file_libio.h:

  • __basic_file 型はパブリックメソッド fd() を持っていて、こいつがファイルデスクリプタを返すっぽい (宣言だけあって定義がない??)

./libstdc++-v3/libio/libio.h:

  • __basic_file は構造体 _IO_FILE を継承したクラスで、_IO_FILE には _fileno という整数型のメンバ変数 (FDっぽい) がある。

というわけで、ofstream からファイルデスクリプタを取り出すには、

ofstream.rdbuf()->_M_file.fd();
ofstream.rdbuf()->_M_file._fileno;

としてやればよさそうだけど、しかし _M_file がプライベートでアクセスメソッドもないのでアクセスできない。basic_filebuf を継承して _M_file へのアクセスメソッドを用意する?? でもそれだとbasic_[io]fstreamも作り直すはめに。それなら最初っから作り直して lock メソッドを用意した方がよさそう??

本日のツッコミ(全3件) [ツッコミを入れる]

fjsg [つmutex]

wassy [んー、最初はmutex使えるのかなぁと思ったんですけどね。でもこれって別々のアプリケーション間での排他制御ってできな..]

wassy [なお、このlockfile.cppもバグ(?)があって、それはclose()してからunlink()するまでの間が不..]


2006-10-27

[プログラミング] Encode.pm, Jcode.pm, Text::Iconv のベンチマーク比較

Encode.pm Jcode.pm のベンチマーク比較 ではEncode.pmとJcode.pmのベンチマークをとっていて便利。ただし、もう一つの変換系であるText::Iconvが含まれていなかったので、追試も兼ねてText::Iconvを含めたベンチマークをやってみる。スクリプトはこんな感じ:

use strict;
use warnings;
use Benchmark ':all';
use Jcode;
use Encode;
use Text::Iconv;
my $text = qq{ASCII, 漢字、カタカナ、ひらがなの混じったtext};

cmpthese(
  sort timethese(
    10000,
    {
      "Jcode::new" => sub {
        $text = Jcode->new($text)->utf8;
        $text = Jcode->new($text)->euc;
      },
      "Jcode::convert" => sub {
        Jcode::convert( \$text, 'utf8', 'euc' );
        Jcode::convert( \$text, 'euc', 'utf8' );
      },
      "Encode::from_to" => sub {
        Encode::from_to($text, "euc-jp", "utf-8");
        Encode::from_to($text, "utf-8", "euc-jp");
      },
      "Text::Iconv" => sub {
        my $cd1 = Text::Iconv->new("euc-jp", "utf8");
        $text = $cd1->convert($text);
        my $cd2 = Text::Iconv->new("utf8", "euc-jp");
        $text = $cd2->convert($text);
      },
    }
  )
);

結果は

Benchmark: timing 10000 iterations of Encode::from_to, Jcode::convert, Jcode::new, Text::Iconv...
Encode::from_to:  0 wallclock secs ( 0.57 usr +  0.00 sys =  0.57 CPU) @ 17543.86/s (n=10000)
Jcode::convert:  1 wallclock secs ( 0.44 usr +  0.00 sys =  0.44 CPU) @ 22727.27/s (n=10000)
Jcode::new:  3 wallclock secs ( 3.38 usr +  0.00 sys =  3.38 CPU) @ 2958.58/s (n=10000)
Text::Iconv:  1 wallclock secs ( 0.43 usr +  0.00 sys =  0.43 CPU) @ 23255.81/s (n=10000)
                   Rate   Jcode::new Encode::from_to Jcode::convert  Text::Iconv
Jcode::new       2959/s           --            -83%           -87%         -87%
Encode::from_to 17544/s         493%              --           -23%         -25%
Jcode::convert  22727/s         668%             30%             --          -2%
Text::Iconv     23256/s         686%             33%             2%           --

と、確かにEncode::from_toは速いんだけど一番速いのはText::Iconvだということがわかる。さらに、モジュールの読み込み時間の差も見てみると、

> time for i in `seq 1 100` ; do perl -MText::Iconv -e '' ; done
real 0m2.702s, user 0m2.440s, sys 0m0.260s

> time for i in `seq 1 100` ; do perl -MJcode -e '' ; done
real 0m3.664s, user 0m2.960s, sys 0m0.290s

> time for i in `seq 1 100` ; do perl -MEncode -e '' ; done
real 0m3.302s, user 0m2.980s, sys 0m0.320s

純粋なperlだけの起動時間は

> time for i in `seq 1 100` ; do perl -e '' ; done
real 0m0.310s, user 0m0.140s, sys 0m0.170s

ってことなので、Encodeが若干遅いことがわかる。

もちろんEncodeは標準で入っているというメリットがあるけど、5.8.0と5.8.1でのバージョン差異とかを考えると、使える人はText::Iconvを使ったほうが良いのかな、という気もします。ただText::Iconvには"fromcode"を指定する必要があるので、文字コードをどこで判別するのかという問題もあるわけですが…。

もっとも、こちらがやろうとしているのはperlのスクリプト作成じゃなくてC++なわけですが、まぁどの処理系を使おうかなぁ、という感じで。

(追記) C++でiconv

実際にiconvを利用するには、こういう使い方が想定されるだろう→「C++でiconv


1980|03|
1986|04|
1998|04|
2002|01|11|
2003|03|04|05|07|08|
2004|01|02|03|04|05|06|07|08|09|10|11|12|
2005|01|02|03|04|05|06|07|08|09|10|11|12|
2006|01|02|03|04|05|06|07|08|09|10|11|12|
2007|02|03|04|06|07|08|11|12|
2008|01|02|03|04|06|07|08|09|10|
2009|01|12|
2011|05|10|11|
2012|01|02|10|