似乎您正在查看 Rx v1.0 实现。主题实现在 Rx v2.0 中进行了彻底检查,以避免分配和调用路径中的重度锁定。同样,Rx 查询管道已根据相同的标准进行了修订。
有关 Rx v2.0 性能改进的更多信息,请参阅http://blogs.msdn.com/b/rxteam/archive/2012/03/12/reactive-extensions-v2-0-beta-available-now.aspx。(虽然这篇文章是从测试版开始的,但大多数信息都适用于 RTM 构建。为了更好,一些事情已经被改进了。)
特别是对于主题,如果您有 2 个或更多的观察者附加,它们通常优于多播代表。在没有附加观察者的情况下,调用观察者的虚拟方法的成本超过了用于事件的空检查和调用模式。通过一个观察者,我们避免了通过调用列表(~ 没有多播部分的委托),但虚拟调用的成本仍然出现。随着更多观察者的连接,我们的虚拟方法的 foreach 循环往往比多播委托背后的机制更快。
这是直接来自 Rx 测试的基准代码的稍微简化的摘录(使一些参数保持不变并删除对内部类型的引用):
var e = default(Action<int>);
var a = new Action<int>(_ => { });
var s = new Subject<int>();
var n = new NopObserver<int>();
var N = 20;
var M = 10000000;
var sw = new Stopwatch();
for (int i = 0; i < N; i++)
{
sw.Restart();
for (int j = 0; j < M; j++)
{
var f = e;
if (f != null)
f(42);
}
sw.Stop();
var t = sw.Elapsed;
Console.WriteLine("E({0}) = {1}", i, t);
sw.Restart();
for (int j = 0; j < M; j++)
{
s.OnNext(42);
}
sw.Stop();
var u = sw.Elapsed;
Console.WriteLine("O({0}) = {1}", i, u);
var d = u.TotalMilliseconds / t.TotalMilliseconds;
Console.ForegroundColor = d <= 1 ? ConsoleColor.Green : ConsoleColor.Red;
Console.WriteLine(d + " - " + GC.CollectionCount(0));
Console.ResetColor();
Console.WriteLine();
e += a;
s.Subscribe(n);
}
在这台机器上,前两次迭代变红;随后的迭代(处理程序计数 >= 2)显示 20%-35% 的加速。但是,所有关于此类基准的常见警告都适用 :-)。
另外,请记住,随着 Rx 中的管道变长,观察者包装的开销(为了安全保证)会下降。这是因为 Rx v2.0 在受信任的运营商之间进行内部握手,避免了额外的包装。只有最终用户订阅才会在 Rx 和用户提供的观察者代码之间进行另一层虚拟调用,以确保正确的异常传播等。在 Rx v1.0 中,为每个操作员提供了一个安全网,添加对于流经每个运营商的每条消息,需要进行 2 到 4 次额外的虚拟呼叫。
简而言之:如果您决定进行任何类型的测试,请选择 Rx v2.0。性能是此版本的第一大功能:-)。