我正在尝试对众多独立数据源进行POC。经典观察者风格应用的排序。数据馈送的数量可能从几百到几千不等,观察者的数量可能会有所不同,从2到20000.以下是简单数据馈送可观察模型的快速示例:
public class FeedMockUp
{
private readonly IScheduler observerScheduler;
private readonly Random rnd = new Random((int)DateTime.Now.Ticks);
private readonly Subject<double> sourceObservable;
private readonly IObservable<double> testedObservable;
public FeedMockUp(IScheduler observerScheduler)
{
this.observerScheduler = observerScheduler;
sourceObservable = new Subject<double>();
testedObservable =
Observable.Create<double>(x =>
{
var underlyingSourceDisposable =
sourceObservable
.Subscribe(_ => x.OnNext(rnd.NextDouble()));
return underlyingSourceDisposable;
});
}
public IDisposable SubscribeToUnderlyingFeed(int numberOfSubscribers)
{
int counter = 0;
var disposable = new CompositeDisposable();
for (int i = 0; i < numberOfSubscribers; i++)
{
disposable.Add(testedObservable
.ObserveOn(observerScheduler)
.Subscribe(_ => Interlocked.Increment(ref counter)));
}
return disposable;
}
public void PushNewFeed()
{
sourceObservable.OnNext(rnd.NextDouble());
}
}
虽然我正在玩shedulers以提高observables更新的吞吐量,但我注意到使用EventLoopScheduler
内存消耗的应用程序有100个数据源和1000个观察者是非常不变的,1000名观察者它是~100Mb并且在向混合添加新观察者时线性增长。
然而,当我尝试使用TaskPoolScheduler时,在x86进程中我开始获得OutOfMemoryException
个异常并且x64进程内存消耗爆炸,或者更确切地说,变得非常不确定,范围从1Gb到2Gb,仅500个观察者随着新观察员的加入,这种情况几乎成倍增长。
这是我用于测试的代码。你能看出它有什么问题吗?为什么性能如此差异?猜猜,这里有一些内部复制/排队,但这只是我的猜测。理想情况下,我想知道这里发生了什么,而不是潜入RX代码库......
private static void Main(string[] args)
{
const int displayItemCount = 100;
const int callbackCount = 500;
//var rtScheduler = new EventLoopScheduler();
var rtScheduler = TaskPoolScheduler.Default;
var rtFeeds = new List<FeedMockUp>();
for (int i = 0; i < displayItemCount; i++)
{
var mockFeed = new FeedMockUp(rtScheduler);
mockFeed.SubscribeToUnderlyingFeed(callbackCount);
rtFeeds.Add(mockFeed);
}
foreach (var rtFeedMockUp in rtFeeds)
{
rtFeedMockUp.PushNewFeed();
}
Console.WriteLine("Memory used for feed {0} mockups with {1} observers / callbacks. Memory {2} Mb",
displayItemCount, callbackCount, Environment.WorkingSet / (1024 * 1024));
Console.ReadKey();
}
答案 0 :(得分:13)
将ObserveOn
与TaskPoolScheduler
一起使用,实际上是为每个观察者创建一个LongRunning任务。
默认TaskScheduler
结束creating a Thread
for each LongRunning
tasks。
每个线程的堆栈大约保留1MB。
因此,使用TaskPoolScheduler
的500名观察者将保留至少500MB。你可以看到它的发展方向......
另一方面,EventLoopScheduler
在单个线程上运行。因此,有效地将ObserveOn
与此调度程序一起使用只会在调度程序的工作队列中添加一个条目。此条目比Thread的1MB成本要小得多。
因此,EventLoopScheduler
对于这种情况来说内存效率要高得多,但是它也会连续地通知观察者,如果有很多观察者并且来源产生的频率很高,那么你将开始累积未发送事件的缓冲区。
TaskPoolScheduler
内存效率较低,但会同时通知观察者,因此可以通过利用计算机上的所有核心来处理比EventLoopScheduler
更高频率的事件。
答案 1 :(得分:6)
您可能想要使用TaskPoolScheduler.Default.DisableOptimizations(typeof(ISchedulerLongRunning))
。如果您不介意失去并行性,EventLoopScheduler是一个不错的选择。
如果您仍希望并行执行工作但想要使用线程池线程,则此选项更可取。