かずきのBlog@hatena

日本マイクロソフトに勤めています。XAML + C#の組み合わせをメインに、たまにASP.NETやJavaなどの.NET系以外のことも書いています。掲載内容は個人の見解であり、所属する企業を代表するものではありません。

見せてもらおうじゃないか、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を手に入れるための代償なのか!まぁリアルタイムに処理をできると思えば、安いもんかな。