CAS 与同步性能

2024-01-12

我已经有这个问题很长一段时间了,试图阅读大量资源并了解正在发生的事情 - 但我仍然无法很好地理解为什么事情是这样的。

简而言之,我正在尝试测试如何CAS将执行 vssynchronized在有竞争和没有竞争的环境中。我已经把这个JMH test:

@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 5, time = 5, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 5, time = 5, timeUnit = TimeUnit.SECONDS)
@State(Scope.Benchmark)
public class SandBox {

    Object lock = new Object();

    public static void main(String[] args) throws RunnerException {
        Options opt = new OptionsBuilder().include(SandBox.class.getSimpleName())
                .jvmArgs("-ea", "-Xms10g", "-Xmx10g")
                .shouldFailOnError(true)
                .build();
        new Runner(opt).run();
    }

    @State(Scope.Thread)
    public static class Holder {

        private long number;

        private AtomicLong atomicLong;

        @Setup
        public void setUp() {
            number = ThreadLocalRandom.current().nextLong();
            atomicLong = new AtomicLong(number);
        }
    }

    @Fork(1)
    @Benchmark
    public long sync(Holder holder) {
        long n = holder.number;
        synchronized (lock) {
            n = n * 123;
        }

        return n;
    }

    @Fork(1)
    @Benchmark
    public AtomicLong cas(Holder holder) {
        AtomicLong al = holder.atomicLong;
        al.updateAndGet(x -> x * 123);
        return al;
    }

    private Object anotherLock = new Object();

    private long anotherNumber = ThreadLocalRandom.current().nextLong();

    private AtomicLong anotherAl = new AtomicLong(anotherNumber);

    @Fork(1)
    @Benchmark
    public long syncShared() {
        synchronized (anotherLock) {
            anotherNumber = anotherNumber * 123;
        }

        return anotherNumber;
    }

    @Fork(1)
    @Benchmark
    public AtomicLong casShared() {
        anotherAl.updateAndGet(x -> x * 123);
        return anotherAl;
    }

    @Fork(value = 1, jvmArgsAppend = "-XX:-UseBiasedLocking")
    @Benchmark
    public long syncSharedNonBiased() {
        synchronized (anotherLock) {
            anotherNumber = anotherNumber * 123;
        }

        return anotherNumber;
    }

}

结果:

Benchmark                                           Mode  Cnt     Score      Error  Units
spinLockVsSynchronized.SandBox.cas                  avgt    5   212.922 ±   18.011  ns/op
spinLockVsSynchronized.SandBox.casShared            avgt    5  4106.764 ± 1233.108  ns/op
spinLockVsSynchronized.SandBox.sync                 avgt    5  2869.664 ±  231.482  ns/op
spinLockVsSynchronized.SandBox.syncShared           avgt    5  2414.177 ±   85.022  ns/op
spinLockVsSynchronized.SandBox.syncSharedNonBiased  avgt    5  2696.102 ±  279.734  ns/op

在非共享的情况下CASis by far更快,这是我所期望的。但在共享的情况下,事情是相反的——这是我无法理解的。我不认为这与偏向锁定有关,因为这会在线程持有锁 5 秒后发生(据我所知),而这种情况不会发生,测试只是证明。

老实说,我希望这只是我的测试是错误的,并且有人有jmh专业知识的出现会告诉我这里的设置是错误的。


主要的误解是假设您正在比较“CAS vs. synchronized”。鉴于复杂的 JVM 如何实现synchronized,您正在比较一个的性能CAS基于算法使用AtomicLong与性能CAS用于实现的基于算法synchronized.

如同Lock,对象监视器的内部信息基本上由int状态表明它是否已被拥有以及嵌套的频率,对当前所有者线程的引用以及等待能够获取它的线程队列。昂贵的方面是等待队列。将线程放入队列、将其从线程调度中删除以及最终在当前所有者释放监视器时将其唤醒,这些操作可能会花费大量时间。

然而,在无竞争的情况下,当然不涉及等待队列。采集监视器由单个CAS将状态从“无主”(通常为零)更改为“拥有,获得一次”(猜测典型值)。如果成功,线程可以继续执行关键操作,然后释放,这意味着仅写入具有必要内存可见性的“无主”状态,并唤醒另一个被阻塞的线程(如果有)。

由于等待队列的成本要高得多,因此即使在竞争情况下,实现通常也会尝试通过执行一定量的旋转来避免它,从而使多次重复CAS在回退到使线程排队之前尝试。如果所有者的关键操作就像单个乘法一样简单,那么监视器在旋转阶段就已经被释放的可能性很高。注意synchronized这是“不公平的”,允许旋转线程立即继续,即使已经有排队的线程等待更长时间。

如果您比较执行的基本操作synchronized(lock){ n = n * 123; }当不涉及排队时al.updateAndGet(x -> x * 123);,你会发现它们大致相当。主要区别在于AtomicLong方法将重复争用乘法,而对于synchronized方法中,如果在旋转期间没有取得进展,则存在被放入队列的风险。

But synchronized allows 锁粗化 https://shipilev.net/jvm-anatomy-park/1-lock-coarsening-for-loops/对于在同一对象上重复同步的代码,这可能与调用syncShared方法。除非还有一种方法可以融合多个CAS的更新AtomicLong,这可以给synchronized一个巨大的优势。 (也可以看看本文 https://www.ibm.com/developerworks/library/j-jtp10185/index.html涵盖了上面讨论的几个方面)

请注意,由于“不公平”性质synchronized,创建比 CPU 核心多得多的线程不一定是问题。在最好的情况下,“线程数减去核心数”线程最终会出现在队列中,永远不会醒来,而其余线程在旋转阶段成功,每个核心上有一个线程。但同样,不在 CPU 核心上运行的线程也不会降低 CPU 的性能。AtomicLong更新,因为它们既不能使当前值对其他线程无效,也不能使失败CAS试图。

无论哪种情况,当CAS在非共享对象的成员变量上或执行时synchronized在非共享对象上,JVM 可以检测操作的本地性质并消除大部分相关成本。但这可能取决于几个微妙的环境因素。


最重要的是,在原子更新和synchronized块。通过更昂贵的操作,事情会变得更加有趣,这可能会增加线程在竞争情况下排队的可能性synchronized,这使得在原子更新的竞争情况下必须重复该操作是可以接受的。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

CAS 与同步性能 的相关文章

随机推荐

  • jekyll:检查是否没有帖子

    如何查看帖子里是否没有帖子 posts folder 到目前为止 我已经尝试过 if site posts null p No posts yet p endif and if site posts nil p No posts yet p
  • 从 VB 转换为 C#

    我的任务是将解决方案从 VB 转换为 C 有 22 个项目和数百个类 所以我决定研究转换器 我最终选择了 SharpDevelop 这是一个带有转换器的 IDE 我在每个项目上都运行了它 并且有很多错误需要修复 但我应该能够检查它们并希望找
  • Sequelize 增量函数返回错误

    尝试增加我的数据库中模型实例的整数字段 这是相关代码 models Options findAll where PollId poll id name option to update then option gt option incre
  • 删除网址中的主题标签,刷新 f5 时出错(angularJs-cordova)

    我对 AngularJs 中的路由器有一些疑问 我使用了 Angular UI Router 但是当使用 locationProvider 删除 url 中的主题标签 时出现错误 如何解决这个问题 在我的模块中 config functio
  • 使用 JNI 从 C++ 调用 JAVA 方法,无参数

    请耐心听我说 我是一名 iPhone 开发人员 而整个 Android 这让我有点困惑 我有一些从 cocos2d x CCMenuItem 调用的 C 方法 因此 根据文档 我无法发送任何参数 我需要使用 android 浏览器打开一个
  • CSS 箭头在 jQuery SlideUp 或 SlideDown 动画期间隐藏

    我有一个使用 CSS 创建的顶部带有箭头的 div arrow box position relative display none background 88b7d5 border 4px solid c2e1f5 padding 20p
  • 在 ExtJS 中,如何循环菜单项?

    如何循环遍历 ExtJS 工具栏菜单中的所有项目 例如更改其图标 使用 each 方法混合系列 http dev sencha com deploy dev docs class Ext util MixedCollection按钮菜单中的
  • 为什么 XmlDocument.LoadXml 抛出 System.Net.WebException?

    为什么System Xml XmlDocument LoadXml方法抛出System Net WebException 这真是令人难以置信的疯狂 如果MSDN http msdn microsoft com en us library s
  • python 中的专用 @property 装饰器

    我有几个类 每个类都有许多属性 所有属性的共同点是它们应该是数字属性 这似乎是使用 python 装饰器的理想场所 但我似乎无法完全理解正确的实现是什么 这是一个简单的例子 class Junk object def init self v
  • 在 VSCode 中包含 d.ts 类型定义文件

    我已经开始使用 VSCode 0 7 0 并注意到注释说你不必添加 不再参考 引入 jsconfig json 后 您不再需要在每个文件中使用 引用 这些在 VS Code 的初始版本中是必需的 由于文件集是在 jsconfig json
  • python Anaconda 中的 Mayavi

    我安装了mayavi在 Anaconda 中使用命令 conda exe install mayavi 在 Anaconda 命令提示符中 现在 当我关闭 Spyder 时 它就不再打开了 我该如何解决 我正在使用Windows 我想展示我
  • 如何避免Python API服务器中的重复处理? [复制]

    这个问题在这里已经有答案了 假设一个函数detect primes调用起来很昂贵 我想避免使用重复的参数重复调用它 我应该怎么办 使用缓存没有帮助 因为该函数可能会在不同的请求中同时调用 当两个请求都将缓存视为空值时 两个请求都将继续执行昂
  • C++ 中的句柄是什么?

    有人告诉我 句柄有点像指针 但其实不是 它允许您保留对对象的引用 而不是对象本身 更详细的解释是什么 句柄可以是从整数索引到指向内核空间中资源的指针的任何内容 其想法是 它们提供了资源的抽象 因此您无需了解太多有关资源本身的信息即可使用它
  • 直接在 Excel 工作表上使用 ActiveX 控件的必要和充分要求是什么?

    Microsoft Office 支持文章 添加或注册 ActiveX 控件 https support office com en us article Add or register an ActiveX control 8fc743a
  • 枚举和 android 注释 intDef

    我有一个枚举 public enum AppEnums SERVICE ERROR CONNECTION ERROR 我想在 Android 注释的 intDef 中使用它 IntDef AppEnums CONNECTION ERROR
  • 在 Kotlin 中实例化对象时如何重写方法?

    在Java中 要在实例化新对象时重写方法 我们可以这样做 public ActivityTestRule
  • 如何在从 Flutter 创建文档到 Firestore 时添加时间戳 [重复]

    这个问题在这里已经有答案了 我正在编写代码以将数据从 flutter 设置到 Firestore 我想要的是添加一个用于创建数据的时间的字段 例如 createdOn Flutter 的 DateTime now 从设备获取时间 但我想获取
  • 简单的Java“新”概念问题

    编译器显示错误new Stock 2 after expect public class TestStockUI Stock stock new Stock 2 stock 0 new Stock Microsoft MSFT 15 69
  • 如何在 SQL Server 2008 上找到禁用的索引

    不久前 当我向 SQL Server 数据库中执行一些批量数据插入时 我禁用了许多索引以提高插入性能 我现在需要返回并重建 重新启用它们 不幸的是 我不确定我禁用了哪些索引 有没有办法可以查询以确定哪些索引被禁用并且应该重新启用 selec
  • CAS 与同步性能

    我已经有这个问题很长一段时间了 试图阅读大量资源并了解正在发生的事情 但我仍然无法很好地理解为什么事情是这样的 简而言之 我正在尝试测试如何CAS将执行 vssynchronized在有竞争和没有竞争的环境中 我已经把这个JMH test