[#34011] Should --verbose be equal to -v ? — Yugui <yugui@...>

Yuguiです。

15 messages 2008/03/10
[#34012] Re: Should --verbose be equal to -v ? — Yukihiro Matsumoto <matz@...> 2008/03/10

まつもと ゆきひろです

[#34105] rational.rb, complex.rb and mathn.rb — Tadayoshi Funaba <tadf@...>

rational と complex が組み込みになったことで、lib/mathn.rb の意義は薄

29 messages 2008/03/22
[#34106] Re: rational.rb, complex.rb and mathn.rb — Tadayoshi Funaba <tadf@...> 2008/03/22

現時点で rational.rb と complex.rb を残しているのは、それが無難だから

[#34107] Re: rational.rb, complex.rb and mathn.rb — Tadayoshi Funaba <tadf@...> 2008/03/22

で、かなり選択肢を絞った叩き台です。

[#34120] Re: rational.rb, complex.rb and mathn.rb — keiju@... (石塚圭樹) 2008/03/24

けいじゅ@いしつかです.

[#34125] Re: rational.rb, complex.rb and mathn.rb — Shin-ichiro HARA <sinara@...> 2008/03/25

原です。

[#34130] Re: rational.rb, complex.rb and mathn.rb — Tadayoshi Funaba <tadf@...> 2008/03/25

> 私も Complex の組み込みは Rational とは比較にならないくらい、仕様が決め

[#34158] Complex組み込み — Masahiro TANAKA <masa16.tanaka@...>

Complexが組み込みになるそうですが、これはcomplex.rbを踏襲して、

49 messages 2008/03/27
[#34161] Re: Complex組み込み — Shin-ichiro HARA <sinara@...> 2008/03/28

原です。

[#34168] Re: Complex組み込み — Tadayoshi Funaba <tadf@...> 2008/03/28

> 今までの Complex は、complex.rb にほぼ残して、たとえば Rational 成分

[#34186] Re: Complex組み込み — Shin-ichiro HARA <sinara@...> 2008/03/31

原です。

[#34187] Re: Complex組み込み — Tadayoshi Funaba <tadf@...> 2008/03/31

> そうです。Complex が難しい、という話を書いておくと、

[#34193] Re: Complex組み込み — Yukihiro Matsumoto <matz@...> 2008/03/31

まつもと ゆきひろです

[#34203] Re: Complex組み込み — Tadayoshi Funaba <tadf@...> 2008/04/01

> |僕としては、/ 演算子の振舞いについて前向きに検討してほしいです。

[#34215] Re: Complex組み込み — Yukihiro Matsumoto <matz@...> 2008/04/02

まつもと ゆきひろです

[#34166] Re: Complex組み込み — Tadayoshi Funaba <tadf@...> 2008/03/28

> となるようですが、別の実装として、

[ruby-dev:34072] rb_memsearch optimization

From: "NARUSE, Yui" <naruse@...>
Date: 2008-03-17 07:20:33 UTC
List: ruby-dev #34072
成瀬です。

現在の rb_memsearch は Karp-Rabin を使っています。
導入の際に Karp-Rabin についておっしゃっていた通り、
確かにパターンが短い場合でも長い場合でもそれなりの性能を示しています。

しかし、パターンの長さによってアルゴリズムの向き不向きが変わるのならば、
長さによってアルゴリズムを変えてはどうでしょう。
具体的にはパターンが 1 バイトの場合はリニアサーチが最速です。
逆に、WORD幅 - 1 バイトを超える場合にはどんどん飛ばしていく BM 系の方が速いでしょう。


さて、今回はパターンを5種類に分け、
1. m > n, m == n, m == 0 等 #=> それぞれ計算
2. m == 1 #=> リニアサーチ
3. 2 <= m <= SIZEOF_VALUE #=> 単純にシフトして比較
4. SIZEOF_VALUE < m <= SIZEOF_VALUE * CHAR_BIT #=> Shift Or
5. SIZEOF_VALUE * CHAR_BIT < m #=> Quick Search
で試してました。

「単純にシフトして比較」は hashing しない Karp-Rabin のような感じです。
1 WORD にバイト列をそのまま、32bit システムなら 4 文字、
64bit システムなら 8 文字格納して比較します。
このレベルの処理だとテーブル参照のコストが以外と効いてくるので、
それなりに差が出てきました。

2. や 3. をいちいち別にする必要があるかという議論はあると思いますが、
rb_memsearch の主たる用途である String#index がもっぱら数文字を引数として
呼ばれることを考えれば、妥当ではないかなぁと。
2. と 3. はだいたい今の 1.5 倍くらいになります。

4. は Shift-Or が効くかと思ったんですが、
実際にはもう Quick Search の方が速くなっていました。

5. は非常に高速なのですが そのまま実装すると UTF-8 の場合が問題でして、
例えば、ひらがなの中からひらがなを探すとかのような、
同じ 1/2 ブロック内での検索だと 3 バイトのうちの 2 バイトがいたるところで
ヒットするので、期待したほどの速度がでなくなります。
そのため、UTF-8 の場合には文字単位で比較するようにしています。
この対策の有無で、単純な Quick Search の最悪のケースから 3 倍ほど変わります。
# 壊れた UTF-8 文字列でもちゃんと動くはずです


これらの対策を施した場合のベンチマークは以下のとおりです。
rad.rb        user     system      total        real
ruby-18  2.140625   0.007812   2.148438 (  2.153251)
ruby-19  2.171875   0.007812   2.179688 (  2.180716)
patched  0.343750   0.007812   0.351562 (  0.358093)

rad8.rb        user     system      total        real
ruby-19  79.687500   0.093750  79.781250 ( 79.725523)
patched  47.640625   0.085938  47.726562 ( 47.701325)

rad.rb は [ruby-core:15508] で Radarek さんが出していたベンチマークです。
この時点で現在同様 Ruby 1.9 は 1.8 と同等の速度になっていましたが、
これがさらに 7 倍速くなります。

rad8.rb は対象のテキストを Ruby 1.9 添付の README.EXT.ja を、
UTF-8 に変換したデータに変えたものです。
こちらでは 1.5 倍になっています。

添付のパッチには一応参考までに Shift-Or を用いたバージョンもつけておきました。

場合分けの手間に見合った速度の向上はあると思うのですが、いかがでしょう。

-- 
NARUSE, Yui  <naruse@airemix.com>
DBDB A476 FDBD 9450 02CD 0EFC BCE3 C388 472E C1EA

Attachments (1)

memsearch.patch (6.46 KB, text/x-diff)
--- include/ruby/encoding.h	(revision 15787)
+++ include/ruby/encoding.h	(working copy)
@@ -176,5 +176,6 @@ int rb_usascii_encindex(void);
 VALUE rb_enc_default_external(void);
 void rb_enc_set_default_external(VALUE encoding);
 VALUE rb_locale_charmap(VALUE klass);
+long rb_memsearch(const void*,long,const void*,long,rb_encoding*);
 
 #endif /* RUBY_ENCODING_H */
--- include/ruby/intern.h	(revision 15787)
+++ include/ruby/intern.h	(working copy)
@@ -466,7 +466,6 @@ double rb_genrand_real(void);
 /* re.c */
 #define rb_memcmp memcmp
 int rb_memcicmp(const void*,const void*,long);
-long rb_memsearch(const void*,long,const void*,long);
 VALUE rb_reg_nth_defined(int, VALUE);
 VALUE rb_reg_nth_match(int, VALUE);
 VALUE rb_reg_last_match(VALUE);
--- re.c	(revision 15787)
+++ re.c	(working copy)
@@ -95,41 +95,202 @@ rb_memcmp(const void *p1, const void *p2
     return memcmp(p1, p2, len);
 }
 
-long
-rb_memsearch(const void *x0, long m, const void *y0, long n)
+static inline long
+rb_memsearch_ss(const unsigned char *xs, long m, const unsigned char *ys, long n)
 {
-    const unsigned char *x = x0, *y = y0;
-    const unsigned char *s, *e;
-    long i;
-    int d;
-    unsigned long hx, hy;
+    const unsigned char *x = xs, *xe = xs + m;
+    const unsigned char *y = ys, *ye = ys + n;
+#ifndef VALUE_MAX
+# if SIZEOF_VALUE == 8
+#  define VALUE_MAX 0xFFFFFFFFFFFFFFFFULL
+# elif SIZEOF_VALUE == 4
+#  define VALUE_MAX 0xFFFFFFFFUL
+# endif
+#endif
+    VALUE hx, hy, mask = VALUE_MAX >> ((SIZEOF_VALUE - m) * CHAR_BIT);
+
+    if (m > SIZEOF_VALUE)
+	rb_bug("!!too long pattern string!!");
+
+    	/* Prepare hash value */
+    for (hx = *x++, hy = *y++; x < xe; ++x, ++y) {
+	hx <<= CHAR_BIT;
+	hy <<= CHAR_BIT;
+	hx |= *x;
+	hy |= *y;
+    }
+    /* Searching */
+    while (hx != hy) {
+	if (y == ye)
+	    return -1;
+	hy <<= CHAR_BIT;
+	hy |= *y;
+	hy &= mask;
+	y++;
+    }
+    return y - ys - m;
+}
 
-#define KR_REHASH(a, b, h) (((h) << 1) - (((unsigned long)(a))<<d) + (b))
+static inline long
+rb_memsearch_so(const unsigned char *xs, long m, const unsigned char *ys, long n)
+{
+    const unsigned char *x = xs, *xe = xs + m;
+    const unsigned char *y = ys, *ye = ys + n;
+    VALUE i, lim, state, table[256];
 
-    if (m > n) return -1;
-    s = y; e = s + n - m;
+    /* Preprocessing */
+    for (i = 0; i < 256; ++i)
+	table[i] = ~0;
+    for (lim = 0, i = 1; x < xe; ++x, i <<= 1) {
+	table[*x] &= ~i;
+	lim |= i;
+    }
+    lim = ~(lim>>1);
+    /* Searching */
+    for (state = ~0; y < ye; ++y) {
+	state <<= 1;
+	state |= table[*y];
+	if (state < lim)
+	    return (y - ys - m + 1);
+    }
+    return -1;
+}
+
+static inline long
+rb_memsearch_kr(const unsigned char *xs, long m, const unsigned char *ys, long n)
+{
+    const unsigned char *x = xs, *xe = xs + m;
+    const unsigned char *y = ys, *ye = ys + n;
+    VALUE hx, hy;
+    long d = sizeof(hx) * CHAR_BIT - 1;
+#define KR_REHASH(a, b, h) (((h) << 1) - (((VALUE)(a))<<d) + (b))
 
     /* Preprocessing */
     /* computes d = 2^(m-1) with
        the left-shift operator */
-    d = sizeof(hx) * CHAR_BIT - 1;
     if (d > m) d = m;
+    /* Prepare hash value */
+    for (hy = hx = 0; x < xe; ++x, ++y) {
+	hx = KR_REHASH(0, *x, hx);
+	hy = KR_REHASH(0, *y, hy);
+    }
+    /* Searching */
+    while (hx != hy || memcmp(xs, y - m, m)) {
+    	if (y == ye)
+	    return -1;
+	hy = KR_REHASH(*(y-d), *y, hy);
+	++y;
+    }
+    return y - ys;
+}
 
-    if (n == m) {
-	return memcmp(x, s, m) == 0 ? 0 : -1;
+static inline long
+rb_memsearch_qs(const unsigned char *xs, long m, const unsigned char *ys, long n)
+{
+    const unsigned char *x = xs, *xe = xs + m;
+    const unsigned char *y = ys, *ye = ys + n;
+    VALUE i, qstable[256];
+
+    /* Preprocessing */
+    for (i = 0; i < 256; ++i)
+	qstable[i] = m + 1;
+    for (; x < xe; ++x)
+	qstable[*x] = xe - x;
+    /* Searching */
+    for (; y < ye; y += *(qstable + y[m])) {
+	if (*xs == *y && memcmp(xs, y, m) == 0)
+	    return y - ys;
+    }
+    return -1;
+}
+
+static inline unsigned int
+rb_memsearch_qs_utf8_hash(const unsigned char *x)
+{
+    register const unsigned int mix = 8353;
+    register unsigned int h = *x;
+    if (h < 0xC0) {
+	return h + 256;
+    }
+    else if (h < 0xE0) {
+	h *= mix;
+	h += x[1];
+    }
+    else if (h < 0xF0) {
+	h *= mix;
+	h += x[1];
+	h *= mix;
+	h += x[2];
+    }
+    else if (h < 0xF5) {
+	h *= mix;
+	h += x[1];
+	h *= mix;
+	h += x[2];
+	h *= mix;
+	h += x[3];
     }
-    /* Prepare hash value */
-    for (hy = hx = i = 0; i < d; ++i) {
-	hx = KR_REHASH(0, x[i], hx);
-	hy = KR_REHASH(0, s[i], hy);
+    else {
+	return h + 256;
+    }
+    return (unsigned char)h;
+}
+
+static inline long
+rb_memsearch_qs_utf8(const unsigned char *xs, long m, const unsigned char *ys, long n)
+{
+    const unsigned char *x = xs, *xe = xs + m;
+    const unsigned char *y = ys, *ye = ys + n;
+    VALUE i, qstable[512];
+
+    /* Preprocessing */
+    for (i = 0; i < 512; ++i) {
+	qstable[i] = m + 1;
+    }
+    for (; x < xe; ++x) {
+	qstable[rb_memsearch_qs_utf8_hash(x)] = xe - x;
     }
     /* Searching */
-    while (hx != hy || memcmp(x, s, m)) {
-	if (s >= e) return -1;
-	hy = KR_REHASH(*s, *(s+d), hy);
-	s++;
+    for (; y < ye; y += qstable[rb_memsearch_qs_utf8_hash(y+m)]) {
+	if (*xs == *y && memcmp(xs, y, m) == 0)
+	    return y - ys;
+    }
+    return -1;
+}
+
+long
+rb_memsearch(const void *x0, long m, const void *y0, long n, rb_encoding *enc)
+{
+    const unsigned char *x = x0, *y = y0;
+
+    if (m > n) return -1;
+    else if (m == n) {
+	return memcmp(x0, y0, m) == 0 ? 0 : -1;
+    }
+    else if (m < 1) {
+	return 0;
+    }
+    else if (m == 1) {
+	const unsigned char *ys = y, *ye = ys + n;
+	for (; y < ye; ++y) {
+	    if (*x == *y)
+		return y - ys;
+	}
+	return -1;
+    }
+    else if (m <= SIZEOF_VALUE) {
+	return rb_memsearch_ss(x0, m, y0, n);
+//    }
+//    else if (m <= SIZEOF_VALUE * CHAR_BIT) {
+//	//return rb_memsearch_kr(x0, m, y0, n);
+//	return rb_memsearch_so(x0, m, y0, n);
+    }
+    else if (enc == rb_utf8_encoding()){
+	return rb_memsearch_qs_utf8(x0, m, y0, n);
+    }
+    else {
+	return rb_memsearch_qs(x0, m, y0, n);
     }
-    return s-y;
 }
 
 #define REG_LITERAL FL_USER5
--- string.c	(revision 15787)
+++ string.c	(working copy)
@@ -2088,7 +2088,7 @@ rb_str_index(VALUE str, VALUE sub, long 
     len = RSTRING_LEN(str) - offset;
     for (;;) {
 	char *t;
-	pos = rb_memsearch(sptr, slen, s, len);
+	pos = rb_memsearch(sptr, slen, s, len, enc);
 	if (pos < 0) return pos;
 	t = rb_enc_right_char_head(s, s+pos, enc);
 	if (t == s + pos) break;

In This Thread

Prev Next