CLR是否执行“锁定省略”优化?如果不是为什么不呢?

问题描述 投票:9回答:3

JVM执行一个称为锁定省略的巧妙技巧,以避免锁定仅对一个线程可见的对象的成本。

这里有一个很好的描述:

http://www.ibm.com/developerworks/java/library/j-jtp10185/

.Net CLR是否有类似的功能?如果不是那么为什么不呢?

java .net multithreading locking clr
3个回答
8
投票

它很整洁,但有用吗?我很难想出一个例子,编译器可以证明锁是本地线程的。默认情况下,几乎所有类都不使用锁定,当您选择锁定时,在大多数情况下,它会从某种静态变量引用,无论如何都会阻碍编译器优化。

另一件事是java vm在其证明中使用了逃逸分析。而AFAIK .net尚未实施逃逸分析。转义分析的其他用途,例如用堆栈分配替换堆分配听起来更有用,应该首先实现。

IMO目前不值得编码。 .net VM中有许多区域没有很好地优化并且具有更大的影响。

SSE向量指令和委托内联是两个示例,我的代码从这个示例中获益远远超过此优化。


4
投票

编辑:正如下面的野蛮指出,这就是说锁定真的很便宜,而不是完全消除它们。我不相信JIT有“线程局部对象”的概念,虽然我可能会弄错......即使它现在没有,它当然可能在未来。

编辑:好的,下面的解释过于简化,但实际上至少有一些基础:)请参阅Joe Duffy's blog post获取更详细的信息。


我不记得我在哪里阅读 - 可能是“通过C#CLR”或“Windows上的并发编程” - 但我相信CLR只在需要时才会懒惰地将对象块分配给对象。当一个对象的监视器从未被争议时被锁定,对象标题将通过比较交换操作进行原子更新,以表示“我已被锁定”。如果一个不同的线程然后尝试获取锁,CLR将能够确定它已经被锁定,并且基本上将该锁升级为“完全”,并将其分配为同步块。

当一个对象具有“完全”锁定时,锁定操作比锁定和解锁其他无争议对象更昂贵。

如果我对此是正确的(并且它是一个非常朦胧的内存),只要锁从不重叠(即没有争用),就可以廉价地锁定和解锁不同线程上的监视器。

我会看看能否为此挖掘一些证据......


3
投票

回答你的问题:不,CLR \ JIT不执行“锁定省略”优化,即CLR \ JIT不会从只对单线程可见的代码中删除锁。这可以通过代码上的简单单线程基准来轻松确认,其中锁定省略应该如您在Java中所期望的那样应用。

可能有很多原因导致它没有这样做,但主要是因为在.Net框架中这很可能是一种不常见的应用优化,因此不值得实施。

同样在.Net无竞争锁定非常快,因为它们是非阻塞的并且在用户空间中执行(JVM似乎对非竞争锁定具有类似的优化,例如IBM)。引用C# 3.0 In A Nutshell's线程章节

锁定速度很快:如果锁定是无竞争的,您可以在3 GHz计算机上以不到100纳秒的速度获取并释放锁定“

可以应用锁省略的几个示例场景,以及为什么它不值得:

在您自己的代码中的方法中使用锁,它完全依赖于本地人

首先在这种情况下使用锁定并不是一个很好的理由,因此与诸如提升循环不变量或方法inling之类的优化不同,这是一种非常罕见的情况,也是不必要使用锁的结果。运行时不应该关注优化不常见和极端恶劣的使用。

使用其他声明为内部使用锁的本地类型

虽然这听起来更有用,但.Net框架的一般设计理念是将锁定责任留给客户端,因此很少有类型具有任何内部锁使用。实际上,当涉及到没有专门设计和广告为并发的类型的实例方法时,.Net框架在病理上是不同步的。另一方面,Java具有包括同步的常见类型,例如StringBuffer和Vector。由于.Net BCL在很大程度上是不同步的,因此锁定省略可能没什么影响。

摘要

我认为总的来说,.Net中锁定省略会引发的案例较少,因为根本没有那么多地方存在线程本地锁。锁更可能用于多线程可见的位置,因此不应被省略。此外,无竞争锁定非常快。

我很难找到任何真实的证据证明锁定elision实际上在Java中提供了很多性能优势(for example...),而且至少Oracle JVM的最新文档表明elision并不总是应用于线程本地锁定,暗示它无论如何,并不总是给出优化。

我怀疑锁定省略是通过在JVM中引入转义分析而实现的,但对于性能并不像EA分析是否可以在堆栈上分配引用类型那样重要。

© www.soinside.com 2019 - 2024. All rights reserved.