かずきのBlog@hatena

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

見せてもらおうじゃないか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と結構健闘してるように見えます。いいかも?