LoginSignup
5
3

More than 3 years have passed since last update.

[Unity] .Net4.xのときにRegexクラスを使うときは独自にキャッシュすることも考えたい

Last updated at Posted at 2019-08-06

動機

Unity で .Net3.5 から .Net4.x に変えただけですごく遅くなるコードが見つかった。プロファイリングをすると Regex クラスのコンストラクタの処理が重いということが分かったので調べたことをまとめる。

動作環境

Unity 2019.1.9f1

内容

次のコードをUnity上で .Net3.5 と .Net4.x それぞれで動作させてみる。 
コードの内容はRegexクラスを生成して文字列を正規表現にかける処理をループで回しているだけ。

using System.Text.RegularExpressions;
using UnityEngine;

public class MyRegex : MonoBehaviour
{
    void Start()
    {
        var str = "hogehoge";
        var sw = new System.Diagnostics.Stopwatch();
        sw.Start();
        for (var i=0; i < 10000; i++)
        {
            var regex = new Regex("^[A-Za-z0-9]+$");
            regex.Match(str);
        }

        sw.Stop();
        Debug.Log("time : " + sw.ElapsedMilliseconds);
    }
}

結果

実行するたびに多少差はでるが、だいたい次のような結果になった。

.Net3.5
time : 36
.Net4.x
time : 613

.Net4.x が .Net3.5 に比べて20倍ほど遅いのがわかる。
(指定したパターン次第でこの差は変わる)

原因調査

Unity-Technologies/monoでコードをみるため、まずmonoのバージョンを確認する。

% /Applications/Unity/Hub/Editor/2019.1.9f1/Unity.app/Contents/Mono/bin/mono --version
Mono JIT compiler version 2.6.5 (tarball Mon Mar 11 12:18:44 GMT 2019)
Copyright (C) 2002-2010 Novell, Inc and Contributors. www.mono-project.com
    TLS:           normal
    GC:            Included Boehm (with typed GC)
    SIGSEGV:       normal
    Notification:  Thread + polling
    Architecture:  amd64
    Disabled:      none
% /Applications/Unity/Hub/Editor/2019.1.9f1/Unity.app/Contents/MonoBleedingEdge/bin/mono --version
Mono JIT compiler version 5.11.0 ((HEAD/2e8093bfb03 Thu Jun 13 15:51:36 GMT 2019)
Copyright (C) 2002-2014 Novell, Inc, Xamarin Inc and Contributors. www.mono-project.com
    TLS:           normal
    SIGSEGV:       altstack
    Notification:  kqueue
    Architecture:  amd64
    Disabled:      shared_perfcounters
    Misc:          softdebug 
    Interpreter:   yes
    LLVM:          supported, not enabled.
    GC:            sgen (concurrent by default)

monoの2.6.5が .Net3.5 で、5.11.0が.Net4.xで使われているmonoになるのかな?

バージョンがわかったのでそれぞれのRegex.csファイルをみる。

  • 2.6.5はmono-2.6.4をみる
    • releaseに同じ番号がなかったので近いバージョン番号のものにした。
  • 5.11.0は最新のRegex.csをみる。
    • releaseでバージョン番号が付けられている最新が mono-4.8.0.374 だったので 5.11.0 を探すのを諦めた。

実装比較

.Net3.5 の方をみると次のような実装になっていた。

Regex.cs(一部抜粋)
        public Regex (string pattern) : this (pattern, RegexOptions.None)
        {
        }

        public Regex (string pattern, RegexOptions options)
        {
            if (pattern == null)
                throw new ArgumentNullException ("pattern");
            validate_options (options);
            this.pattern = pattern;
            this.roptions = options;
            Init ();
        }
:
: 省略
:

#if !TARGET_JVM
        private void Init ()
        {
            this.machineFactory = cache.Lookup (this.pattern, this.roptions);

最新版の方をみる。

Regex.cs(一部抜粋)

        public Regex(String pattern)
            : this(pattern, RegexOptions.None, DefaultMatchTimeout, false) {
        }
:
: 省略
:
        private Regex(String pattern, RegexOptions options, TimeSpan matchTimeout, bool useCache) {
:
: 省略
:
                if (useCache)
                    cached = CacheCode(key);
  • .Net3.5のほうはコンストラクタ内でキャッシュ機構があり、同じパターンに対してはキャッシュが効く。
  • 最新版の方はキャッシュ機構があるにはあるが、使われていなかった。
    • private のコンストラクタに bool useCache で指定ができるがこちらを true にするpublicなコンストラクタがなかった

.Net3.5では同じパターンに対してはキャッシュ機構が働いて初期化処理が軽かったが.Net4.xはキャッシュ機構がないため処理が重くなっていた。

コードの改良

独自にキャッシュをすれば .Net3.5 と .Net4.x で速度はそこまで変わらないものにできるはずなので試す。

MyCachedRegex.cs
using System.Text.RegularExpressions;
using UnityEngine;

public class MyCachedRegex : MonoBehaviour
{
    Regex regex = new System.Text.RegularExpressions.Regex("^[A-Za-z0-9]+$");

    void Start()
    {
        var str = "hogehoge";
        var sw = new System.Diagnostics.Stopwatch();
        sw.Start();
        for (var i = 0; i < 10000; i++)
        {
            regex.Match(str);
        }
        sw.Stop();
        Debug.Log("cached time : " + sw.ElapsedMilliseconds);
    }
}

改良後の結果

Net3.5
cached time : 31
Net4.x
cached time : 17

.Net4.x の処理速度が .Net3.5 より早くなることが確認できた。

まとめ

.Net4.x のときに Regex クラスを使うときは同じパターンを何度も使う場合はキャッシュして使うようにしたい。

.Net3.5 から .Net4.x に変更しただけで既存のコードで負荷がかかる箇所が変わっている可能性があるので注意したい。

5
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
3