CAS 是一个像旋转一样的循环吗?
Is CAS a loop like spin?
我在阅读sun.misc.Unsafe.Java
的代码时遇到了一个问题。
CAS 是一个像旋转一样的循环吗?
起初,我认为CAS只是一种低寿命的原子操作。但是,当我尝试查找函数compareAndSwapInt
的源代码时,我发现 cpp 代码如下所示:
jbyte Atomic::cmpxchg(jbyte exchange_value, volatile jbyte* dest, jbyte compare_value) {
assert(sizeof(jbyte) == 1, "assumption.");
uintptr_t dest_addr = (uintptr_t)dest;
uintptr_t offset = dest_addr % sizeof(jint);
volatile jint* dest_int = (volatile jint*)(dest_addr - offset);
jint cur = *dest_int;
jbyte* cur_as_bytes = (jbyte*)(&cur);
jint new_val = cur;
jbyte* new_val_as_bytes = (jbyte*)(&new_val);
new_val_as_bytes[offset] = exchange_value;
while (cur_as_bytes[offset] == compare_value) {
jint res = cmpxchg(new_val, dest_int, cur);
if (res == cur) break;
cur = res;
new_val = cur;
new_val_as_bytes[offset] = exchange_value;
}
return cur_as_bytes[offset];
}
我在这个原子函数中看到了"何时"和"中断"。
是旋转方式吗?
相关代码链接:
http://hg.openjdk.java.net/jdk8u/jdk8u20/hotspot/file/190899198332/src/share/vm/prims/unsafe.cpp
http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/07011844584f/src/share/classes/sun/misc/Unsafe.java
http://hg.openjdk.java.net/jdk8u/jdk8u20/hotspot/file/55fb97c4c58d/src/share/vm/runtime/atomic.cpp
CAS
是一个返回值1
或0
的单个操作,这意味着此操作是否已成功,因为您正在执行您希望此操作成功的compareAndSwapInt
,因此操作会重复直到它起作用。
我认为您还将此与spin lock
混淆,这基本上意味着在此值为"1"时执行某些操作(例如);所有其他线程等到该值为零(通过compareAndSwap
),这实际上意味着某个线程已完成工作并释放了锁(这称为release/acquire
语义)
CAS 操作不是旋转,而是硬件级别的原子操作。在 x86 和 SPARC 处理器上,CAS 只有一条指令,它支持int
和long
操作数。
实际上,Atomic::cmpxchg
int
/long
重载是使用单个cmpxchgl
/cmpxchgq
指令在x86上生成的。
您看到的是一个Atomic::cmpxchg
单byte
重载,它绕过了 CAS 指令在byte
级别模拟 CAS 的限制。它通过对与byte
位于同一地址的int
执行CAS来实现,然后只检查其中的一个byte
,如果CAS由于其他3个字节的更改而失败,则重复。比较和交换仍然是原子的,只是有时需要重试,因为它覆盖的字节数超过了必要的字节数。
CAS通常是一个硬件指令,就像整数加法或比较一样(只是速度较慢)。指令本身可以分解为所谓的微码的几个步骤,并且可能确实包含低级循环或对另一个处理器组件的阻塞等待。但是,这些是处理器体系结构的实现细节。还记得CS中的任何问题都可以通过添加另一层间接来解决的说法吗?这也适用于这里。Java 中的原子操作实际上可能涉及以下层:
- Java 方法签名。
- 一个 C(++) JNI 方法来实现它。
- C(++)"编译器内在的",如GCC的
__atomic_compare_exchange
- 实际的处理器指令。
- 实现此指令的微码。
- 所述微码所使用的附加层,例如缓存一致性协议等。
我的建议是不要担心所有这些工作原理,除非任何一种情况都适用:
- 由于某种原因,它不起作用。这可能是由于平台错误。
- 它太慢了。
单元测试可以帮助您识别前一种情况。基准测试可以帮助您识别后一种情况。但应该指出的是,如果 Java 提供给您的 CAS 很慢,那么您可能无法自己编写更快的 CAS。因此,在这种情况下,最好的办法是更改数据结构或数据流,例如进一步减少所需的线程同步量。
- 如何循环打印顶点结构
- 如何在C++中从两个单独的for循环中添加两个数组
- C++我的数学有什么问题,为什么我的代码不能正确循环
- 正在尝试了解输入验证循环
- std::map<struct,struct>::find 找不到匹配项,但是如果我循环通过 begin() 到 end(),我在那里看到匹配项
- 循环后如何继续阅读
- Ardunio UNO解决了多个重叠的定时器循环
- Eigen如何在容器循环中干净地附加矩阵
- 在某些循环内使用vector.push_back时出现分段错误
- 我正在使用嵌套的while循环来解析具有多行的文本文件,但由于某种原因,它只通过第一行,我不知道为什么
- 为什么我的for循环不能正确获取argv
- 如何声明特征矩阵,然后通过嵌套循环初始化它
- while循环中while循环的时间复杂度是多少
- C++中的高效循环缓冲区,它将被传递给C样式数组函数参数
- GCC 优化了基于固定范围的 for 循环,就好像它具有更长的可变长度一样
- 像在 Python 中一样C++循环中的参数解析。为什么不起作用?
- CAS 是一个像旋转一样的循环吗?
- 是一个 for 循环,最终甚至不会运行一次,就像调用 if 语句以首先检查大小一样快
- Do iota、generate和手卷循环的执行都是一样的
- c++中的循环和python中的一样(基于范围的for)