Ich versuche , eine große aufzuzählen IEnumerable
einmal, und beobachten Sie die Aufzählung mit verschiedenen Betreibern angebracht ( Count
, Sum
, Average
usw.). Der naheliegende Weg besteht darin, es IObservable
mit der Methode in eine umzuwandeln ToObservable
und dann einen Beobachter zu abonnieren. Mir ist aufgefallen, dass dies viel langsamer ist als andere Methoden, z. B. eine einfache Schleife ausführen und den Beobachter bei jeder Iteration benachrichtigen oder stattdessen die Observable.Create
Methode verwenden ToObservable
. Der Unterschied ist erheblich: Es ist 20-30 mal langsamer. Es ist was es ist oder mache ich etwas falsch?
using System;
using System.Diagnostics;
using System.Linq;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using System.Reactive.Threading.Tasks;
public static class Program
{
static void Main(string[] args)
{
const int COUNT = 10_000_000;
Method1(COUNT);
Method2(COUNT);
Method3(COUNT);
}
static void Method1(int count)
{
var source = Enumerable.Range(0, count);
var subject = new Subject<int>();
var stopwatch = Stopwatch.StartNew();
source.ToObservable().Subscribe(subject);
Console.WriteLine($"ToObservable: {stopwatch.ElapsedMilliseconds:#,0} msec");
}
static void Method2(int count)
{
var source = Enumerable.Range(0, count);
var subject = new Subject<int>();
var stopwatch = Stopwatch.StartNew();
foreach (var item in source) subject.OnNext(item);
subject.OnCompleted();
Console.WriteLine($"Loop & Notify: {stopwatch.ElapsedMilliseconds:#,0} msec");
}
static void Method3(int count)
{
var source = Enumerable.Range(0, count);
var subject = new Subject<int>();
var stopwatch = Stopwatch.StartNew();
Observable.Create<int>(o =>
{
foreach (var item in source) o.OnNext(item);
o.OnCompleted();
return Disposable.Empty;
}).Subscribe(subject);
Console.WriteLine($"Observable.Create: {stopwatch.ElapsedMilliseconds:#,0} msec");
}
}
Ausgabe:
ToObservable: 7,576 msec
Loop & Notify: 273 msec
Observable.Create: 511 msec
.NET Core 3.0, C # 8, System.Reactive 4.3.2, Windows 10, Konsolen-App, Release erstellt
Update: Hier ist ein Beispiel für die tatsächliche Funktionalität, die ich erreichen möchte:
var source = Enumerable.Range(0, 10_000_000).Select(i => (long)i);
var subject = new Subject<long>();
var cntTask = subject.Count().ToTask();
var sumTask = subject.Sum().ToTask();
var avgTask = subject.Average().ToTask();
source.ToObservable().Subscribe(subject);
Console.WriteLine($"Count: {cntTask.Result:#,0}, Sum: {sumTask.Result:#,0}, Average: {avgTask.Result:#,0.0}");
Ausgabe:
Anzahl: 10.000.000, Summe: 49.999.995.000.000, Durchschnitt: 4.999.999,5
Der wichtige Unterschied dieses Ansatzes gegenüber der Verwendung von Standard- LINQ- Operatoren besteht darin, dass die aufzählbare Quelle nur einmal aufgelistet wird.
Noch eine Beobachtung: Die Verwendung ToObservable(Scheduler.Immediate)
ist etwas schneller (ca. 20%) als ToObservable()
.