无锁数据结构中ABA问题的一个流行解决方案是使用额外的单调递增标记来标记指针。
struct aba {
uintptr __ptr;
};
uint32_t get_tag(struct aba aba) { return aba.__ptr >> 48U; }
然而,这种方法存在问题。它真的很慢,并且存在巨大的缓存问题。如果我抛弃标签字段,我可以获得两倍的加速。但这不安全吗?
所以我下一次64位平台的尝试填充了ptr字段中的位。
MAP_32BIT
但有人对我说,标签只有16位是不安全的。我的新计划是使用指针对齐缓存行来填充更多的标记位,但我想知道它是否有效。
如果无效,我的下一个计划是使用Linux的mmap
angular.module('myApp', [])
.controller('MyController', ['myService', function (myService) {
// Do something with myService
}]);
标志来分配数据,所以我只需要32位指针空间。
无锁数据结构中ABA标记需要多少位?
答案 0 :(得分:4)
可以根据抢占时间和指针修改频率估算实际上安全的标记位数。
要提醒一下,ABA问题发生在一个线程通过compare-and-swap读取它想要更改的值,被抢占时,并且当它恢复时,指针的实际值恰好等于线程之前读取的值。因此,尽管在抢占时间内可能由其他线程完成数据结构修改,但比较和交换操作可能会成功。
添加单调递增标记的想法是使指针的每个修改都是唯一的。为了使它成功,增量必须在修改线程被抢占的时候产生唯一的标记值;即为了保证正确性,标签在整个抢占期间可能不会环绕。
假设抢占持续一个OS调度时间片,通常是几十到几百毫秒。 CAS在现代系统上的延迟是几十到几百纳秒。因此粗略的最坏情况估计是在线程被抢占时可能有数百万个指针修改,因此标记中应该有20多个位以便它不会环绕。
在实践中,可以基于已知的CAS操作频率对特定的实际用例做出更好的估计。还需要更准确地估计最坏情况下的抢占时间;例如,被优先级较高的作业抢占的低优先级线程可能会以更长的抢占时间结束。
答案 1 :(得分:3)
根据论文
http://web.cecs.pdx.edu/~walpole/class/cs510/papers/11.pdf 危险指针:无锁物体的安全存储器回收(IEEE并行和分布式系统交易,第15卷,第6期,2004年6月,第491页),博士Maged M. Michael
标记位的大小应该在真正无锁的场景中使得环绕不可能(我可以读取这个,好像你可能有N个线程正在运行,每个都可以访问结构,你应该至少有N + 1个不同的标记状态):
6.1.1 IBM ABA-Prevention标签
节点重用的最早,最简单的无锁方法是 引入的标签(更新计数器)方法 IBM System 370上的CAS文档[11]。它 需要将标记与每个位置相关联 ABA倾向比较操作的目标。通过递增 关联位置的值为时的标记 写入,比较操作(例如,CAS)可以确定是否 该位置是自上次访问后写的 相同的线程,从而防止ABA问题。 该方法要求标记包含足够的位 在任何执行期间完全环绕不可能 单次无锁定尝试。这种方法非常有效 允许立即重用已退役的节点。
答案 2 :(得分:2)
根据您的数据结构,您可以从指针中窃取一些额外的位。例如,如果对象是64字节并且始终在64字节边界上对齐,则每个指针的低6位可用于标记(但这可能是您已经为新计划建议的内容)。
另一种选择是在对象中使用索引而不是指针。
在连续对象的情况下,当然只是数组或向量的索引。如果列表或树在堆上分配了对象,则可以使用自定义分配器并在已分配的块中使用索引。
对于17M的对象,你只需要24位,为标签留下40位。
这需要一些(小的和快速的)额外计算来获得地址,但是如果对齐是2的幂,则只需要移位和添加。