limitusus’s diary

主に技術のことを書きます

libsqlite3.so を差し替える

ちょっと聞かれたので調べてみた。

目的

オリジナル libsqlite3.so.0 を実装したので、これを /module/libsqlite3.so.0 とし、 Python から使えるようにしたい。
Python

import sqlite3

とすると最終的に /usr/lib/python2.7/lib-dynload/_sqlite3.so が読まれるので、

ldd _sqlite3.so

としたときに libsqlite3.so.0 が /module/libsqlite3.so.0 を指すようにすればよい。

普通なら

動的ライブラリなんだから LD_LIBRARY_PATH を設定するのがまずは常識。

LD_LIBRARY_PATH=/module

しかし変わらない。

RPATH

実は _sqlite3.so には RPATH という情報が書かれていて、これが動的ライブラリの場所を示している。
manpage dlopen(3) を読んでもらうとわかるが、動的ライブラリはまずこの RPATH を検索し、ここにあればこれを使う。
LD_LIBRARY_PATH が読まれるのはその次の優先順位なのである。
したがって、この場合 LD_LIBRARY_PATH は機能しない。

RPATH を消す

以下の情報は多分に Frank's Geo-Geeking: RPATH, RUNPATH and LD_LIBRARY_PATH を参考にしている。
RPATH 情報は chrpath(8) により見ることができる。

chrpath /usr/lib/python2.7/lib-dynload/_sqlite3.so 
/usr/lib/python2.7/lib-dynload/_sqlite3.so: RPATH=/usr/lib/i386-linux-gnu

ここで気付いた。
_sqlite3.so の RPATH がないバージョンを作り、これを Python から読むようにすれば?」

cp /usr/lib/python2.7/lib-dynload/_sqlite3.so /module/_sqlite3.so
chrpath --delete /module/_sqlite3.so
export LD_LIBRARY_PATH=/module
ldd /module/_sqlite3.so

これで /module/_sqlite3.so が /module/libsqlite3.so.0 を読むようになってくれた。
もちろん Python から使うときには

export PYTHONPATH=/module

も忘れずに。

ちなみに

libpthread.so.0 ではこんな問題は起きない。というのも、libpthread.so.0 は RPATH で指定されたディレクトリではないところにあるからである。
この問題は SQLite3 モジュールが Python 組込みであることによっているのではないだろうか。

iccをインストールした記録

Intel C コンパイラって買わないと使えないのかと思ってたけど、非商用ならば無料でインストールできることがわかったので実際にやってみた。

ダウンロードまで

これは 2011 年 11 月 18 日の操作記録だが、サイト更新に伴いページ構成が変更されたりリンクが切れる可能性がある。

C Compiler & C++ Compiler Suite from Intel - Intel® Software Network

この辺に IntelC/C++ 系開発用ソフトウェアがまとめられていた。
画面上部の Tools & Downloads から Free Non-Commercial というのを選ぶ。

Non-Commercial Software Development - Intel® Software Network

ここで非商用利用の規約に同意する。

その後いくつかのソフトウェアを選択できる。ここでは Intel® C++ Composer XE 2011 for Linux を選択した。
あとは必要事項を入力すればダウンロードできるようになる。

ライセンス番号もさりげなく画面に表示されるので忘れずに控えておく。

インストー

tarball を落としてきたので、適当に展開し、その中の ./install.sh を実行する。
あとは画面の指示に従ってライセンス番号やインストール先を指定していけばいい。
root 権限があればシステム領域にインストールできるし、今回はユーザ領域($HOME/intel 以下)にインストールした。
RPM 系のディストリビューションではなかった & tarball の中には rpm ファイルもあったが、特に問題なくインストールできた。

lookupDictionary.js を直してみた

とっても便利な Vimperator プラグインの lookupDictionary.js。
どうやら ALC 側で XPath が変更されてしまったらしく結果が帰ってこなくなってしまったため、簡単に修正。

Index: lookupDictionary.js
===================================================================
--- lookupDictionary.js	(リビジョン 37220)
+++ lookupDictionary.js	(作業コピー)
@@ -13,7 +13,7 @@
     names: ['eiji[ro]'],
     url: 'http://eow.alc.co.jp/%s/UTF-8/',
     shortHelp: 'SPACE ALC (\u82F1\u8F9E\u6717 on the Web)',
-    xpath: 'id("resultList")',
+    xpath: 'id("resultsList")',
     dictionary: 'en-US'
 },{
     names: ['goo[dictionary]'],

快適な辞書ライフを!

Python で signal と threading を両立してみた

経緯

最近書いているプログラムで無限ループするワーカースレッドを立てまくるものがあって, それを signal で安全に終了させる手段が知りたかった.

課題プログラム

以下のようなプログラムがあります.

import threading
import time
def loopfunc(event):
    print "Thread Started"
    while not event.isSet():
       time.sleep(1)
    print "Thread End"

def main() :
    threads = []
    e = threading.Event()
    for x in range(10):
        threads.append(threading.Thread(target=loopfunc, args=(e,)))
        threads[x].start()
    for th in threads:
        th.join()

main()

これを起動すると10個のスレッドが作られて, 無限ループします. これに対して SIGINT などを送っても何も起きません. これは Python の thread 全体に対していえることらしいです. たとえば明示的にシグナルハンドラを作って

import threading
import time
import signal
def loopfunc(event):
    print "Thread Started"
    while not event.isSet():
       time.sleep(1)
    print "Thread End"

def sighandler(event, signr, handler):
    event.set()

def main() :
    threads = []
    e = threading.Event()
    signal.signal(signal.SIGINT, (lambda a, b: sighandler(e, a, b)))
    for x in range(10):
        threads.append(threading.Thread(target=loopfunc, args=(e,)))
        threads[x].start()
    for th in threads:
        th.join()

main()

としても, やはり SIGINT は無視されます. これはなぜかというと, join() を呼んでいる最中は signal を受け付けないからです.

解決策

これは絶対に誰か出会った問題だろうと思って調べてみたら(下の参考ページ参照), 開始直後に os.fork() してシグナルハンドラを登録し, スレッドのマスターに SIGKILL を送信することによって解決するという方法が掲載されていました. しかしさすがにこれは乱暴すぎるような気がしたので, もう少し穏やかな終了方法を考えてみました. ふと見るとこれ, 先輩のページだ!

import threading
import time
import signal
def loopfunc(event):
    print "Thread Started"
    while not event.isSet():
       time.sleep(1)
    print "Thread End"

def sighandler(event, signr, handler):
    event.set()

def main() :
    threads = []
    e = threading.Event()
    signal.signal(signal.SIGINT, (lambda a, b: sighandler(e, a, b)))
    for x in range(10):
        threads.append(threading.Thread(target=loopfunc, args=(e,)))
        threads[x].start()
    for th in threads:
        while th.isAlive():
            time.sleep(0.5)
        th.join()

main()

このように 0.5 秒に1回, スレッドの生存を確認することによって join() を呼ばず, シグナルを受け付けられるようにしてみました. この他にも join([timeout]) に引数を渡して isAlive() で調べるループにするという方法もアリだと思います.

これがベストなのかどうかは分かりませんが, やりたいことは実現できたのでメモ.

Embarrassingly Parallel なプログラムを pthread で並列化してみた

やったこと

後輩の授業で Embarrassingly Parallel なプログラムを並列化するという課題が出ていたので、ちょっとやってみました。

実際に使ったコードはgithubで公開中です。公開は課題の締切後に行いました。

課題1 1次元関数の数値積分区間分割による並列化

実関数 $f(x)=sin(x)$ が与えられ、$\int_0^{\pi/2} f(x) dx$ を区分求積法で求めるプログラムがあったとき、区間 $(0, \frac{\pi}{2})$ を N 分割し、 N 個の POSIX thread に割り当てて並列化するという問題です。

この課題は練習用で、プログラムは公開されていました。単に走らせて性能を測ればよいものでした。

課題2.1 素数探索プログラムの区間分割による並列化

2から10000000までの整数のうち、素数がいくつあるかを数えるプログラムが与えられたとき、区間を N 個に分割し、 N 個の POSIX thread で並列化する問題です。課題1のコードを利用すればできる比較的簡単な課題です。

課題2.2 素数探索プログラムの負荷分散を考慮した並列化

素数の分布は一様ではないため、課題2.1のコードでは負荷が偏ってしまいます。そこで区間を M 分割し、 N 個の POSIX thread を利用することで並列化するのが本課題です。

やりかたは単純で、

  1. 区間を M 個に分割し、それぞれをタスクとして作る。
  2. N 個の POSIX thread を作る。

というものです。各 thread は

for j in range(0 .. M-1) :
  if (j 番目のタスクは終わっていない) :
    j 番目のタスクをやる

とふるまいます。これに排他制御を行います。タスクを表す構造体に「手を付けた」というフラグを追加します。

for j in range(0 .. M-1) :
  lock(mutex)
  if (j 番目のタスクは誰も手を付けていない) :
    j 番目のタスクに手を付ける
    unlock(mutex)
    j 番目のタスクをやる
  else :
    unlock(mutex)

このようにすれば OK です。

続きを読む

「事業仕分けに対する緊急声明」を聞いてきた

大学にいる人間として(, そして一利害関係者として), ちょっと真面目に生意気なことを書いてみます.

何があったのか

大学でノーベル賞・フィールズ賞受賞者による事業仕分けに対する緊急声明と 科学技術予算をめぐる緊急討論会 - お知らせ - 東京大学 大学院理学系研究科・理学部というイベントがあったので, 行ってみました. 私の立場はせいぜい研究室に所属する学部学生であって, 研究室の先生が予算を取ってくれていて, そのお金によって研究室の運営ができている, その恩恵にあずかっている存在です. そんな学部学生ですが, 基礎研究(Science)および科学技術(Science-based Technology)に関する予算が削減されることが「ヤバい」ことくらいは直感で分かっているつもりです. 私の周囲には研究者, 技術者の方が多いので眺めてみても, この直感は外れていなかったと思っています. その中で, ノーベル賞, フィールズ賞を受賞なさった教授陣が声明を発表したわけです.

前日(11月24日)には国立大学の学長クラス*1が記者会見を開いていて(記者会見「学術・大学関連予算について」の開催 【共同声明】大学の研究力と学術の未来を憂う −国力基盤衰退の轍を踏まないために−), これが開かれただけでも「相当な異常事態だな」とは思っていたのですが, これに加えて更に開かれたわけで, これは深刻な状態だと感じました.

どうやら反応として「ノーベル賞を受賞した学者を出すのは思考停止だ」とか, 「金融を軽視してる」とかいう意見が存在しているようですが, 前者はそれなりに狙った部分もあるようです. しかし, 研究というのは百発百中ではないし, 投げてみるまでどれが当たるのか分からない性質があり, その中である意味で「たまたま当たった」人がノーベル賞受賞者なわけです. その「当たる/当たらない*2」の線を体感している方々が声明を出すというのは, 大きな意味があると思います. 次に後者ですが, 「声明文では科学技術創造立国が目標であるとなっているが, 我が国に金融分野での発展はないのか」という質問に対する, 「金融は知は産まない. 財は産むけど」という返答に反応した意見だと思います. しかし経済学と区別された金融は知を産んでいるかというと, 果たしてそうなのか?という疑問が(少なくとも私には)あります. また, 東大には元々金融に関する研究機関はなかったはずで, 2005年に初めて金融システム専攻ができ, 2年前から経済学部の中で経済学科/金融学科が分かれるようになった程度だと認識しています. そういう意味で東大としては「金融分野は学術領域として成熟しておらず, 研究実績がない」という立場だったのかな, と思っています. 会場では質問そのものが「『理系内閣』と言われた内閣のはずなのに科学技術の予算が削られる」ことを問題視している議論の最中に金融の話がピンボケだと感じられ, そこにうまい返しが入ったことから拍手が起きた, ということだと思います. それは金融を軽視しているとかバカにしているとかではなく, 「対象とする領域が全然違う」ということでしょう. とはいえ, あの部分だけ聞けばそう思うのももっともな話なので, 配慮すべきだったと思いますが…

討論会としては, 壇上で喋りすぎて質問の機会が少なすぎたのでは?と感じました. また, 会場が狭すぎて入りきれていなかったので, もう少し広い会場が用意できたらよかったのかな, と感じました.

声明

以下のページに声明文の全文が掲載されています.

横山広美のお知らせページ : 声明文

重要なことしか書かれていない*3ので全部重要なのですが, 予算が途切れる→その間研究者がいなくなる→科学/技術が低迷→予算を付ける→学ぼうとする人ができても, それを教えられる人が不足する, という状況が起きて, それは取り返しがつかない. 現在の「事業仕分け」と称した作業はそれを引き起こしかけてるよ, ということです. 私はこれは至極もっともなことだと考え, 署名してきました.

署名

会場にいらっしゃらなかった方でも, 現在 Web 上から署名を行うことができます. 声明に賛同できる方はどうぞ.

署名簿: ノーベル賞・フィールズ賞受賞者による事業仕分けに対する緊急声明

*1:慶應義塾大学は「慶應義塾長」と呼ぶため

*2:外れたって言い方は適切じゃないですよね

*3:不要なものは排除してあります!