星期三, 2月 14, 2007

HyperEstraier 的速度問題

這是拿 strace 仔細追 estmaster 的時候發現的問題,用的參數是 strace -o output.txt -p PID -ttt。(沒有用 ktrace 是因為 kernel 沒編進去)

主要的問題有兩個,影響比較大的是在 estmaster.c 裡面有一堆 est_usleep() 造成不必要的 nanosleep(),這個部份可以簡單的 comment 掉加速。

另外一個問題是在 est_sock_recv_line() 這個 function 裡面,讀取一行的方法是一次讀 1 byte,這會使得速度被拖慢 (雖然沒有上一個多),不過這個部份得改寫所有透過 socket 處理的 code,並不是那麼容易生出 patch...

總而言之, 頗有 woju 的味道...

星期二, 12月 05, 2006

HyperEstraier 與 qdbm 的一些雜記

的使用了 的 Database 的部份以外,還使用了自己寫的輪子,cabin(3)。

這套輪子功能相當多,提供了許多 Data Structure Function 與 Algorithm 使用,不過如果真的要用的話,我也許會選擇其他 的 library (),像是 libisc (從 裡抽出來的)。

這次遇到 看起來有 memory-leak 的問題是 cabin(3) 的 cbmapout() 產生的。cbmap* 是處理 Hash 的 Data Structure Function,cbmapout() 會將元素從 Hash 中移出,不過我測試的結果發現移出後並不會釋放記憶體,於是就會造成 memory-leak 的「假象」,不過實際上這些記憶體會重複被使用,並沒有 memory-leak 的情況發生。這可能是因為 resize 的 overhead 考量而故意設計的。

所以塞資料進 要注意在塞一定的數量後對每個 node 下一次 sync 指令,強迫將快取的內容寫回硬碟後清掉,另外一個方法是把上一篇對 estmaster.c 的 patch 拿掉,這樣理論上在固定一段時間沒有動作後就會寫回去。(不過就不能一次大量寫進去,快取的大小會超過 process size...)

星期日, 12月 03, 2006

HyperEstraier

Open Source 的 Fulltext Search Engine 其實還蠻多套的,不過對於 UTF-8 環境來說,Fulltext Search 真正的問題在於非拉丁語系的切詞方式,而非底層的技術。

拉丁語系的切詞只要對標點符號及空白的地方切開,但非拉丁語系的語言 (像是 CJK) 並沒有辦法這樣做,解決的方法有幾種:最簡單的就是直接去找各國語言的詞庫 (以及頻率表),然後切詞,但這個方法對於新產生的詞沒有辦法處理,而且對於我們,不知道要如何取得非中文的詞庫。另外一種方法是完全透過 Algorithm 處理切詞,這樣我們就不用想辦法取得其他語言的詞庫,也不用管新的名詞出現。

我最近幫的忙是一個中文站台要對文章做全文搜尋 (UTF-8 環境,所以還是要考慮有少量的外國語言),目前站上的數量大約有 1 million 的文章要處理,文章數量會不斷成長。

當時在找的時候找到幾套軟體,最後因為切詞問題決定使用 ,一方面是作者相當活躍 (與 QDBM 同一個作者),另外一方面作者是日本人,我們認為日文與中文跑出來的效果應該不會差太多... (純粹是希望 :p)

FreeBSD 上安裝 沒什麼問題,直接到 裡安裝就可以了,不過我們測試的結果發現有些地方需要改掉,以免影響 performance 太多。

我把 patch 檔放成 FreeBSD ports patch 的格式,所以只要把這兩個檔案放到 /usr/ports/textprocs/hyperestraier/files/ 裡,再透過 ports 安裝就可以了。第一個檔案是 patch-estmaster.c
--- estmaster.c.orig    Sat Dec  2 19:42:59 2006
+++ estmaster.c Sat Dec 2 19:44:45 2006
@@ -1258,6 +1258,7 @@
}
est_usleep(1000 * 100);
}
+#if 0
if(isec >= g_idleflush * (1.0 - g_cacheratio) &&
(g_cacheratio > 0.0 || isec % (g_idleflush / 2 + 1) == 0)){
if(g_stmode){
@@ -1301,6 +1302,7 @@
}
est_usleep(1000 * 100);
}
+#endif
}
free(stfile);
log_print(LL_INFO, "shutting down");

第二個檔案是 patch-estraier.c
--- estraier.c.orig     Sat Nov 11 16:26:03 2006
+++ estraier.c Thu Nov 30 22:53:10 2006
@@ -2310,7 +2310,9 @@
}
}
db->dnum++;
+#if 0
if(est_db_used_cache_size(db) > db->icmax && !est_db_flush(db, INT_MAX)) err = TRUE;
+#endif
return err ? FALSE : TRUE;
}

其他的就照官方網站上所說的用法就可以了。另外一個很重要的資源是 存取 HyperEstraier 的方式,我自己是透過 處理,測試起來沒什麼問題... :p

星期三, 11月 01, 2006

一些 C Macro 的技巧 (Part III:將傳入的參數名稱變化)

假設你想要把傳入參數名稱再變化,用 ## 穿插其中:
#define print_three_var(var)
do {
print_var(var);
print_var(var##2);
print_var(var##3);
} while (0)

因為你不能用 var2 來表示 var + "2",所以你必須用 ##。當你傳入 print_three_var(telephone) 時,他會展開成:
print_var(telephone);
print_var(telephone2);
print_var(telephone3);

補充一下:這邊的 print_var() 是 Part II 裡的 print_var()

一些 C Macro 的技巧 (Part II:將傳入的參數變成字串)

另外一個技巧是將傳入的參數變成字串:
#define print_var(var)     do {         printf("%s: %s\n", #var, var);     } while (0)

關鍵字是 #var。當輸入 print_var(argv[0]); 時就會把以上的 Macro 展開為 printf("%s: %s\n", "argv[0]", argv[0]);

一些 C Macro 的技巧 (Part I:不定變數)

幾個學校課程不太會講到的技巧,第一個是不定變數的使用:
#ifdef DEBUG
#define debug_printf(str, ...) do { printf(str, __VA_ARGS__); } while (0)
#else
#define debug_printf(str, ...)
#endif

關鍵是 __VA_ARGS__,這樣可以很愉快的使用 debug_printf()

星期一, 10月 30, 2006

Perl 的 reference

perlref(5) 是蠻重要的文件,講到 reference 要怎麼用:
$scalarref = \$foo;
$arrayref = \@ARGV;
$hashref = \%ENV;
$coderef = \&handler;
$globref = \*foo;

看起來不難,不過下面馬上就提到陷阱,這兩個其實是一樣的:
@list = (\$a, \@b, \%c);
@list = \($a, @b, %c);

解法是用 [] 做。

星期一, 10月 23, 2006

Math::Polynomial::Solve 的用法

Perl 上有很多東西都有人做過了,所以能儘量去 CPAN 上找就去找,可以省下很多功夫... (自己寫一個還得測試有沒有問題)

Math::Polynomial::Solve 正是其中的一個例子,我們知道五次以上的方程式沒有公式解,但可以用其他的方法找,於是就有人寫了出來,舉例來說,我要找 x^5 + 2 = 0,那麼我的參數就是 (1, 0, 0, 0, 0, 2),丟進去以後他就會給你解:
use Math::Complex;
use Math::Polynomial::Solve qw/poly_roots/;
my @x = poly_roots(1, 0, 0, 0, 0, 2);
foreach (@x) { print $_, "\n"; }

結果應該是:
0.929316490603147-0.675187952399881i
0.929316490603147+0.675187952399881i
-0.35496731310463-1.09247705577745i
-0.35496731310463+1.09247705577745i
-1.14869835499703

星期六, 10月 21, 2006

Curve Fitting on Perl

CPAN 上的 Algorithm-CurveFit 很好玩 :p

先給定一堆 (x, y) 後,再給定已知的方程式 (可以是非線性的) 與要尋找的參數,然後他就可以幫你算 Least Squares Fitting... (不知道這是什麼的,可以參考 MathWorld 上的 Least Squares Fitting 這篇,這是一種統計方法,可以找最相似的原始參數)

不過非線性方程式的速度有點慢,五萬筆資料計算二次的 Fitting 在 Dual Pentium III 1.266Ghz (Tualation) 上跑了 16xx 秒 CPU time,果然是純用 Perl 硬幹出來的東西... (沒有用到 xs 寫 C 加速)

不過還是蠻好用的,所以我就包一包弄進 FreeBSD ports,等這陣子 ports unfrozen 過後就會進去了:ports/104620: [NEW PORT] math/p5-Algorithm-CurveFit: Nonlinear Least Squares Fitting

星期一, 5月 29, 2006

FreeBSD 上 rpc.lockd 的問題

FreeBSD 上 rpc.lockd 常常會自己掛掉。如果是 clients 的 rpc.lockd 掛掉,會使得 flock() over NFS 掛掉,而如果是 servers 的 rpc.lockd 掛掉,會使得所有 clients 的 flock() over NFS 掛掉 =_=

這個問題自從我們將某幾台 ccbsd*.csie clients 換成 FreeBSD 6.0-RELEASE 後就發現這幾台一直有問題,後來決定將 cchome.csie 與 mailgate.csie 也一起升級到 FreeBSD 6.0-RELEASE,看看能不能解決這個問題。結果反而變成大家都有問題... -_- 而且 ccsun*.csie 只要一開機,所有的 clients 就會炸掉,但這些 ccsun*.csie 裡面,如果只開 ccserv.csie 也不會有事情...。

這個問題當時猜測是 FreeBSD 4.x 與 6.x 的 clients 混在一起跑造成的,於是狠下心來將 ccbsd*.csie 全部升級到 FreeBSD 6.0-RELEASE (另外一個原因是 FreeBSD 4.x 已經不繼續維護了),結果升級上去發現問題還是沒變好,只好試著惡搞所有能惡搞的功能 (像是 mount_nfs 裡的 -L local lock),發現還是沒有用。

前幾天想到 ktrace 可以看 system call 的情況,拿出來追,發現 uid=daemon 的 rpc.lockd 最後居然是死在 SIGPIPE,將這個 bug 用 send-pr 送出去幹剿後,rpc.lockd 的 maintainer 丟出一個神秘的 patch:把 SIGPIPE 用 signal(SIGPIPE, SIG_IGN) 堵掉,#bsdchat 上面看到這個 patch 把他叫做「掩耳盜鈴」,當作沒聽到,幹就對了 XD:Problem Report bin/97768 : NFS rpc.lockd will die automatically

雖然這個方法看起來很鳥,但我還是決定把這個 patch 弄進所有 ccbsd*.csie 上跑,然後開 ktrace 盯著看還會有什麼死法。跑了快一天,發現 ccbsd*.csie 跑起來好像不錯,寫了封信跟 maintainer 講... 沒多久就看到 cvs commit 記錄:cvs commit: src/usr.sbin/rpc.lockd kern.c,結果才剛 commit 完沒多久就發現 uid=root rpc.lockd 死掉 XD

看了一下 uid=root rpc.lockd 死掉時 ktrace 的紀錄,發現也是 SIGPIPE 的問題,正覺得奇怪,明明 mask 掉了,怎麼還會死在 SIGPIPE... 回頭看 patch 發現是因為 signal()fork() 後才做,所以 uid=root 那隻沒有處理 SIGPIPE,只好再跟 maintainer 講,問看看能不能把 signal() 放到 fork() 前。(主要是他對 rpc.lockd 比較熟,可能會比較清楚各類後遺症 XD)

結果 maintainer 就叫我去測測,沒問題就照我說的 patch 進 -current XD 我就跑去跟 chwong 要了 cchome.csie 與 mailgate.csie 的 root,要跑 ktrace 盯著看。

測了一天看起來沒問題:rpc.lockd 沒死,mutt 看信也都很正常,看起來 locking 功能運作得很好,就回封信跟 maintainer 講這個情況,過不久就看到他把 signal() 搬到 fork() 前的 cvs commit 紀錄:cvs commit: src/usr.sbin/rpc.lockd kern.c,這個 patch 的 rpc.lockd 到現在都還活得很好,看起來在 6/1 那天就會進 RELENG_6 了 :)

然後在 #bsdchat 上看到 rafan 把這個 patch 也丟進 csie.ntu 的機器上跑,發現對於 NFSv2 的 Sun 也正常多了 (打開 debug mode 還是會看到一堆 error msg,不過至少會動...),於是我剛剛就很高興的跑去學校把 ccsun*.csie 通通開起來測,目前看起來也都沒問題...

折騰了兩個月,NFS 的 rpc.lockd 這件事情看起來暫時穩定多了,希望有長輩把 rpc.lockd 修一修,尤其是 recovery 的部分,如果寫完的話才能隨意關機開機 XD