かずきのBlog@hatena

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

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

前回Enumerable.Range vs Observable.Range + Publishを試してみました。結果としては、当然なんですがRxのほうが遅いということになりました。それはそうとして、本来分配するタイプじゃないものを無理やり分配するんじゃなくて、もともと分配するように作られてるSubjectでやったら・・・?ということを思いついたので下記コードで試してみました。

using System;
using System.Diagnostics;
using System.Linq;
using System.Reactive.Linq;
using System.Reactive.Subjects;

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();
        });
        // Observableで10万個の最小、最大、平均
        Watch("Subject", () =>
        {
            var o = new Subject<int>();
            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);
                });
            foreach (var i in Enumerable.Range(1, 1000000).Select(_ => r.Next(1000)))
            {
                o.OnNext(i);
            }
            o.OnCompleted();
        });
    }

    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.402945
Enumerable : 68
min: 0, max: 999, avg: 499.205099
Observable : 7337
min: 0, max: 999, avg: 499.686331
Subject : 230

EnumerableとObservableで100倍近い差があるのは前回の通りなのですがSubjectでやった場合は230msと結構健闘してるように見えます。いいかも?