65.9K
CodeProject 正在变化。 阅读更多。
Home

统一并发 IV - 跨平台

starIconstarIconstarIconstarIconstarIcon

5.00/5 (2投票s)

2019年3月16日

CPOL

5分钟阅读

viewsIcon

8426

downloadIcon

68

面向 .NET 和 .NET Core 的跨平台面向对象同步原语方法,基于通用线程和 Async/Await 两个接口之间的一个共享模式。

系列文章

引言

今天,我想宣布 Unified Concurrency 已经实现跨平台!
Unified Concurrency 现在兼容 .Net Standard 2.0,确保在 .NET 4.7+ 和 .NET Core 2.0+、Mono 5.4+ 中可用。

近期 .NET 世界的跨平台开发似乎势不可挡,从 .Net Standard 2.0 来看,该平台的采用率在开源和商业领域似乎都得到了加速。这为 Unified Concurrency 实现跨平台提供了极大的动力。我仍然打算尽可能地保留 .Net 4.6 版本,但主要开发已转移到 .Net Standard 项目。

将库 GreenSuperGreen(包含 Unified Concurrency)迁移到 .Net Standard 2.0 也需要对整个基准测试和交叉基准测试库进行相同的更新,从而有机会在 .NET / .NetCore 以及可能的 Mono 上运行基准测试、交叉基准测试和跨平台基准测试。对 Linux 进行基准测试会很有趣,但目前基准测试依赖于 PerformanceCounters,而它们是平台相关的(Windows),并且这个问题似乎在 .Net Standard 的未来版本中仍然悬而未决。

包含所有已实现同步原语的示例,可在 .NetCore 2.1 下的单元测试项目中开始使用。

Unified Concurrency 框架 在开源的 GreenSuperGreen 库中实现,可在 GitHubNuget 上获取。

.NET 4.6
NetStandard 2.0
NetCore 2.1
Net 4.7.2

更多同步原语

现在可以使用另外 3 个同步原语,还有 1 个仅供内部使用(用于基准测试)。

AsyncSemaphoreSlimLockUC : IAsyncLockUC

基于 SemaphoreSlim WaitAsync/Release 的锁,似乎能很好地实现 FIFO 风格的公平访问。

性能上与 AsyncLockUC 相似。

SemaphoreSlimLockUC : ILockUC

基于 SemaphoreSlim Wait/Release 的锁,采用混合方法和原子指令,无法很好地实现 FIFO 风格,不公平访问可能导致线程停滞

SemaphoreLockUC : ILockUC

基于 Semaphore WaitOne/Release 的锁,依赖于操作系统,在 Windows 上大致为 FIFO,不保证公平性

MutexLockUC : ILockUC - 内部使用,仅用于基准测试

此同步原语不可访问,仅供预定义的基准测试项目使用,因为它要求进入和退出调用具有线程亲和性,这在 Unified Concurrency 的设计中不支持,但对于基准测试而言,它是可维护且有趣的,可以收集数据。

.NET Core 加速

根据微软、外部来源和技术社区的报告,.NET Core 可以加速现有代码库,这已成为普遍认知。

通过跨平台基准测试,我可以在两个方面报告改进。

.NET Core 加速:JIT 编译

在基准测试场景中,吞吐量周期的顺序基线是衡量同一硬件上潜在加速的有用工具,而该代码在 .Net Core 2.1 上的速度比 .Net 4.7.2 快 1.997 倍。这并不意味着所有代码都会快这么多倍,只是某些代码可以更有效地进行 JIT 编译并因此运行得更快,但潜在的加速总是代码相关的,有时可能无法进一步优化。 Stephen Toub 也曾 报道过某些特定情况下的类似加速

图 1:顺序吞吐量加速 .NET / .NET Core(加速与代码相关)

.NET Core 加速:C# lock - Monitor 类改进

跨平台基准测试结果显示,在重负载场景和不良邻居场景下,C# lock(Monitor 类)有显著改进。

.NET 实现容易出现 CPU 僵死,即 C# lock(Monitor 类)浪费大部分 CPU 资源,而完成的工作却很少,同步成本占据了大部分 CPU 资源。这在之前的文章中已有报道。

.NET Core 2.1 似乎对 C# lock(Monitor 类 / .NET Core 2.1 运行时中的 C++ AwareLock 类)有更好的实现。

图 2:.Net 和 .Net Core 上 C# lock(Monitor 类)的 CPU 资源浪费,以及 .Net Core 上的 LockUC。

根据图 2,很容易得出 .NET Core 2.1 在性能上取得优势的地方。C# 锁通常分布在大多数项目的代码中,是许多常用库(包括运行时本身)的一部分,在这里,我们在某些时间情况下看到了显著的 CPU 资源改进,CPU 浪费减少了 80% 以上!请比较 C# lock Monitor 类的蓝色和绿色趋势线。

JIT 改进很重要,但充斥着 C# 锁的多线程代码在许多项目中仍然普遍存在,从简单的代码到业务线代码库。
即使是简单的项目,性能提升也可能相当可观,而且升级到 .NetCore 3.0 并获得 WinForms 和 WPF 的访问权限可能非常吸引人。

这是 .NetCore 2.1 的一项重要改进,但仍有改进空间。例如,我们可以考虑 LockUC,请比较绿色和红色趋势线,这表明仍有约 10% 的提升空间,但这通常是以在 1 毫秒吞吐量周期以下的吞吐量略有下降为代价的,这时基于原子指令的同步原语可以通过保持合理的 CPU 浪费来帮助提高吞吐量,但这需要现代架构设计考虑到多核时代。

跨平台基准测试

图 3:C# lock(Monitor) / LockUC 在重负载场景下的交叉基准测试,.NET 4.7.2,16 核。

图 4:C# lock(Monitor) / LockUC 在重负载场景下的交叉基准测试,.Net Core 2.1,16 核。

摘要

本文作为 GreenSuperGreen 库(内置 Unified Concurrency)的 .NET Standard 2.0 版本发布公告,并包含了一些库的改进。

我们通过跨平台基准测试讨论并展示了从 .NET 升级到 .NET Core 2.1+ 的巨大潜力和动力,这得益于 JIT 编译的改进以及 C# lock(Monitor 类)在减少 CPU 浪费方面的改进,从而改善了多线程代码。

修订历史

  • 2019年3月16日:初版
  • 2019年3月24日:修正了一些拼写错误和增加了 2 个交叉基准测试
© . All rights reserved.