かずきのBlog@hatena

すきな言語は C# + XAML の組み合わせ。Azure Functions も好き。最近は Go 言語勉強中。日本マイクロソフトで働いていますが、ここに書いていることは個人的なメモなので会社の公式見解ではありません。

見せてもらおうじゃないか、Reactive Extensionsの性能とやらを!!

ということで、昨年のクリスマスイブのid:neueccさんの記事を読んで気になったので試してみました。

記事の締めくくりに下記のように書いてあります。

注意しないといけないのは、ふつーのシーケンスがあって、それをRxで分配やるとはっきし言って遅いです。
列挙数が多くなると、ただ配列舐めるのに比べて数万倍のパフォーマンス差が出たりします。

ということで、ざくっと以下のようなコードを書いてみました。もちろんneueccさんの記事にある通り、このケースがRxに向かないのは承知の上で書いてます。

namespace ConsoleApplication5
{
    using System;
    using System.Diagnostics;
    using System.Linq;
    using System.Reactive.Linq;

    class Program
    {
        static void Main(string[] args)
        {
            var r = new Random();
            // Enumerableで10万個の最小、最大、平均
            Watch("Enumerable", () =>
            {
                var array = Enumerable.Range(1, 1000000).Select(_ => r.Next(1000)).ToArray();
                var min = array.Min();
                var max = array.Max();
                var avg = array.Average();

                Console.WriteLine("min: {0}, max: {1}, avg: {2}", min, max, avg);
            });

            // Observableで10万個の最小、最大、平均
            Watch("Observable", () =>
            {
                var o = Observable.Range(1, 1000000).Select(_ => r.Next(1000)).Publish();
                o.Min().Zip(o.Max(), (min, max) => new { min, max })
                    .Zip(o.Average(), (x, avg) => new { x.min, x.max, avg })
                    .Subscribe(v =>
                    {
                        Console.WriteLine("min: {0}, max: {1}, avg: {2}", v.min, v.max, v.avg);
                    });
                o.Connect();
            });
        }

        static void Watch(string tag, Action action)
        {
            var s = Stopwatch.StartNew();
            try
            {
                action();
            }
            finally
            {
                s.Stop();
                Console.WriteLine("{0} : {1}", tag, s.ElapsedMilliseconds);
            }
        }
    }
}

こいつを、うちのマシンで実行してみると以下の通り。

min: 0, max: 999, avg: 499.774453
Enumerable : 75
min: 0, max: 999, avg: 499.299666
Observable : 7349

100倍くらい遅いのね!!これがReactiveを手に入れるための代償なのか!まぁリアルタイムに処理をできると思えば、安いもんかな。