.NET Micro Framework で文字置換を高速に行うには

  • このエントリーをはてなブックマークに追加
  • Evernoteに保存Evernoteに保存
high-speed-char-replacing-on-dotnet-mf

先日「.NET Micro Framework で string.Replace 文字列置換を使うには」で紹介したCommon ExtensionsのReplaceメソッドですが、結構時間がかかります。

あまりに置換の数が多いと、動作に支障がでるので、ここでは一文字(char)に限って高速に置換できるメソッドを実装します。といっても単純ですが。

ちなみに実行環境は Netduino Plus、.NET Micro Framework 4.1 です。

スポンサーリンク

Replaceの概要とStringBuilderの問題

Common ExtensionsのReplaceメソッドは引数にstring型の文字列を2つとります。こんな感じで。

これで “A” を “B” という文字列に置き換えてくれます。内部的には同じCommon Extensionsに含まれるStringBuilderクラスを使っているようです。
問題は、textの中に含まれる “A” の数にもよりますが、数十ミリ秒~数百ミリ秒はかかってしまうことです。

原因はこのStringBuilderです。単純な文字列結合より高速なパフォーマンスがでてほしいのですが、特に結合回数が少ない場合はこのNetMf.CommonExtensions.StringBuilderはstringの+演算子より遅いです。

試しに “a” という1バイトの文字列と、”abc” という3バイトの文字列をどんどん結合していく(どんどん後ろに足していく)テストをしたところ、下記のようになりました。

回数 所要時間 [ms]
1文字 “a” 3文字 “abc”
string += StringBuilder string += StringBuilder
100 15 50 19 54
200 35 100 54 106
300 60 152 105 158
400 90 201 172 215
500 125 251 260 270
600 164 303 372 327
700 209 353 491 386
800 257 403 606 443
900 305 452 779 498
1000 362 504 957 562

ご覧のとおり、1バイトの結合では、stringの結合のほうが速いです。ただ、3バイトになると600回ぐらいからStringBuilderが追い抜きます。しかし、普通は500回も繰り返すことはないと思うので、いずれにしろ単純なstringの結合でよさそうですね。

ということで、内部的にStringBuilderを使っているため、Replaceも遅いのは仕方ないようです。

1バイト置換専用のReplaceを作る

とはいえ、たとえばURLに含まれたスラッシュ “/” をパスのバックスラッシュ “\” に変換したい場合など、1文字を置き換えたいことも多いです。このときにいちいち上記のReplaceメソッドを使っていたのでは、オーバーヘッドが大きくなりすぎてしまいます。

そこで、1バイトのみ置換する用のReplaceメソッドを拡張メソッドで作成しました。

引数の型はchar型にしてあるので、既存の(CommonExtensionsの)Replaceとは競合しないはず。実装としては非常に単純で一旦char配列に変換して、配列の要素を置換後、再びstringに戻して返す、というだけです。

使うときはこんな感じ。char型なのでシングルクォーテーション ‘ になっていることに注意。

で、せっかくなのでこれらもベンチマークをとりました。

置換個数 所要時間 [ms]
CommonExtensionsの
Replace
char専用
Replace
100 453 30
200 1534 61
300 3237 89
400 5574 121
500 8639 150

うーん、劇的に違いますね。これでずいぶん快適になりました。500個置換して150msならさほど問題にならないでしょう。

最後までお読みいただきありがとうございました m(_ _)m

ベンチマーク用のソースコード

最後に今回ベンチマークに使ったソースを載せておきます。参考まで。

Stopwatchは経過時間を計測するだけのクラスなので、適当に作ってください。

StringBuilder と string += 比較用

1000バイト単位の文字列操作を繰り返しているとすぐにOutOfMemoryExceptionが発生するので、1ループごとにDebug.GCで強制的にガベージコレクトさせています。

CommonExtensionsのReplaceと自作Replace比較用

スポンサーリンク
  • このエントリーをはてなブックマークに追加
  • Evernoteに保存Evernoteに保存