[pgsql-jp: 35215] EUC-SJIS変換の改善について
a_ogawa
a_ogawa @ hi-ho.ne.jp
2005年 4月 8日 (金) 21:31:15 JST
小川と申します。
internalな話題ですが,日本語処理の話題なので日本語で議論できればと
思い,投稿させていただきます。
(PostgreSQLのしくみ分科会へも投稿したので、重複して受信したかたは
ご容赦ください)
encodingがEUCのデータベースに対して,WindowsなどのSJISクライアントから
多量のデータを検索したとき,文字コードの変換処理がボトルネックになることが
あります。
そこで,EUC->SJIS変換の効率を改善するパッチを作成してみました。
このパッチが新たなバグを作り出していないか?,もっと速くできないか?など
ご意見お待ちしています。
テスト方法:
(1)以下のようなSQLファイル(test.sql)を用意する。
set client_encoding to 'SJIS';
select * from accounts;
(accountsは,pg_bench -iで作成されるテーブルです)
(2)SQLを実行する。
$ psql -f test.sql -o /dev/null
(3)サーバプロセスのprofile結果
Each sample counts as 0.01 seconds.
% cumulative self self total
time seconds seconds calls s/call s/call name
14.17 0.17 0.17 9088966 0.00 0.00 pg_mule_mblen
10.00 0.29 0.12 400008 0.00 0.00 euc_jp2mic
8.33 0.39 0.10 9088966 0.00 0.00 pg_mic_mblen
5.83 0.46 0.07 1301673 0.00 0.00 AllocSetAlloc
5.83 0.53 0.07 400009 0.00 0.00
perform_default_encoding_conversion
5.83 0.60 0.07 400008 0.00 0.00 mic2sjis
5.00 0.66 0.06 1300705 0.00 0.00 AllocSetFree
pg_mule_mblen, euc_jp2mic, pg_mic_mblen, mic2sjis,
perform_default_encoding_conversionはEUC->SJIS変換で実行される関数です。
さらに,AllocSetAlloc, AllocSetFreeの実行回数のうち約2/3は,変換用の
一時的なバッファ確保のために使われています。
つまり今回のテストケースでは,実行時間の大半が文字コード変換に使われて
いることになります。
特にpg_mule_mblenやpg_mic_mblenは,call回数が非常に多くなっています。
このパッチでは,これらの関数の実行回数を減らすようにしてみました。
パッチの内容は以下のとおりです。
(1)mic2sjisのループ内部で,pg_mic_mblenを実行しないようにした
オリジナルのコードでは,1文字ごとにpg_mic_mblenを実行して残りの文字の
長さを計算しています。さらに,pg_mic_mblenはpg_mule_mblenを実行します。
このため,pg_mic_mblen/pg_mule_mblenの実行回数が多くなっています。
while (len >= 0 && (c1 = *mic))
{
len -= pg_mic_mblen(mic++);
これを以下のようにすることで,ループ内部でpg_mic_mblenを実行する必要が
なくなります。
unsigned char *mic_end = mic + len;
while (mic <= mic_end && (c1 = *mic))
{
mic++;
(2)mic2sjisでSJISに変換できない文字が現れたとき,micポインタの指す位置が
正しくならないときがあるのではないか?
else if (c1 > 0x7f)
{
/* cannot convert to SJIS! */
*p++ = PGSJISALTCODE >> 8;
*p++ = PGSJISALTCODE & 0xff;
ここでは現在の文字のバイト数を考慮していないため,micが次の文字を指して
いることを保証できないと思います。
そこで,micの位置を調節するために以下のコードを追加しました。
/*
* Adjust a mic pointer. Because can't guarantee mic points
* next char here.
*/
mic--;
mic += pg_mic_mblen(mic);
効果の測定(1): patch適用後のprofile結果
Each sample counts as 0.01 seconds.
% cumulative self self total
time seconds seconds calls ms/call ms/call name
11.46 0.11 0.11 400008 0.00 0.00 euc_jp2mic
10.42 0.21 0.10 1301673 0.00 0.00 AllocSetAlloc
6.25 0.27 0.06 1300705 0.00 0.00 AllocSetFree
6.25 0.33 0.06 400000 0.00 0.00 FunctionCall3
6.25 0.39 0.06 100000 0.00 0.00 slot_deform_tuple
5.21 0.44 0.05 400008 0.00 0.00 euc_jp_to_sjis
4.17 0.48 0.04 900051 0.00 0.00 appendBinaryStringInfo
長い実行時間を占めていたpg_mule_mblenやpg_mic_mblenがいなくなり,
ボトルネックがeuc_jp2micへ移動したことが分かります。
効果の測定(2): 実行時間の測定
profileオプションを外して(-O2のみにする),postgresをリビルドしてから
以下のコマンドで実行時間を測定。
$ time psql -f test.sql -o /dev/null
-- patch適用前
real 0m2.964s
user 0m0.850s
sys 0m0.110s
-- patch適用後
real 0m2.639s
user 0m0.820s
sys 0m0.060s
実行時間(real)が約10%程,短縮できています。
今回作成したpatch fileやprofile取得のメモなどは、以下のURLにおいて
ありますので興味のある方はご参照ください。
http://www.hi-ho.ne.jp/a_ogawa/memo/pg/euc_jp_and_sjis/
---
小川 淳 (a_ogawa @ hi-ho.ne.jp)
pgsql-jp メーリングリストの案内