[rub][source] +とかconcatのソースを読む
Rubyレシピブックを久々に見てみたら、concat処理は早く、+は遅いとかって感じのことがかかれてた(garbage collectionの関係とか)ので、ソースコードを読んでみた。
はじめに、+の処理部から読んでみる。なお、rubyのメソッドは、rb_define_method()関数部分で登録されてる。
- は以下のようにちなみに、以下のような感じで登録されています
rb_define_method(rb_cString, "+", rb_str_plus, 1);
上の関数でのrb_str_plusが+の実体なので、その処理部分を読んでみると、以下のような感じになってました。
/* * call-seq: * str + other_str => new_str * * Concatenation---Returns a new <code>String</code> containing * <i>other_str</i> concatenated to <i>str</i>. * * "Hello from " + self.to_s #=> "Hello from main" */ VALUE rb_str_plus(str1, str2) d(rb_cString, "+", rb_str_plus, 1); VALUE str1, str2; { VALUE str3; StringValue(str2); str3 = rb_str_new(0, RSTRING(str1)->len+RSTRING(str2)->len); memcpy(RSTRING(str3)->ptr, RSTRING(str1)->ptr, RSTRING(str1)->len); memcpy(RSTRING(str3)->ptr + RSTRING(str1)->len, RSTRING(str2)->ptr, RSTRING(str2)->len); RSTRING(str3)->ptr[RSTRING(str3)->len] = '\0'; if (OBJ_TAINTED(str1) || OBJ_TAINTED(str2)) OBJ_TAINT(str3); return str3; }
VALUE(unsigned long) str3という新しい要素を作成し、それにstr1+str2とした文字列を代入しreturnしてるのがわかる。
これに対して、concatは以下のような感じになっています。
rb_define_method(rb_cString, "concat", rb_str_concat, 1); rb_define_method(rb_cString, "<<", rb_str_concat, 1);
concat = <<っぽいですね。
実際の処理はrb_str_concatで処理してるので、どのような処理がなされてるかみてみます
/* * call-seq: * str << fixnum => str * str.concat(fixnum) => str * str << obj => str * str.concat(obj) => str * * Append---Concatenates the given object to <i>str</i>. If the object is a * <code>Fixnum</code> between 0 and 255, it is converted to a character before * concatenation. * * a = "hello " * a << "world" #=> "hello world" * a.concat(33) #=> "hello world!" */ VALUE rb_str_concat(str1, str2) VALUE str1, str2; { if (FIXNUM_P(str2)) { int i = FIX2INT(str2); if (0 <= i && i <= 0xff) { /* byte */ char c = i; return rb_str_cat(str1, &c, 1); } } str1 = rb_str_append(str1, str2); return str1; }
rb_str_plusみたいに、str3を新しく作らずに、str1にstr2を追加した文字列を返してることが分かります。
str1にstr2を追加させてるrb_str_append()関数の内部での処理では、新たにstrlen(str2)の領域をstr1に追加させるためにrealloc処理をしてからstringを返す処理が行われています。(str2が数値の場合はascii文字コードに変換され、値がrb_str_cat()で返されます。)
str2が数値の場合の例はこんな感じ
p "tako".conccat(126) 出力 "tako~"(126のascii文字は~)
まぁ、無駄な領域を作ってるだけに処理が遅くなるってことでしょうか。しかし、レシピブックの30秒と、0.05秒ってのの差は驚異的だなぁ.. メモリはできるだけ節約しましょうってことですかね。
mallocの処理も結構時間がかかるって話を聞いたことがあるのだけど、reallocはそうでもないのかな?謎だ