热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

TicketLock的RelaxedAtomics优化

原文地址作者:PedroRamalhete,译者:周可人,校对:梁海舰Ticklock是mutuallock的

原文地址 作者:Pedro Ramalhete,译者:周可人,校对:梁海舰

Tick lock是mutual lock的一种简单实现:

http://web.mit.edu/6.173/www/currentsemester/readings/R06-scalable-synchronization-1991.pdf

它是由John Mellor-Crummey和Michael Scott在1991年提出的(pdf中2.2节),感谢C++11和C11中新的内存模型,我们可以对单个变量进行具有屏障或者不具有屏障的原子操作。当屏障没有使用,只有原子性保证时,我们称之为“relaxed atomic”:

http://en.cppreference.com/w/cpp/atomic/memory_order

注意在C11/C++11内存模型中,一个原子变量并不具有内在的“顺序一致性”或者“relaxed”性质,然而我们可以在每次访问的时选择它的行为。

原子修饰符只能保证原子性,即这个变量会被单步读或写。其他语言,如Java和Scala则不同,它们可以保证几乎所有的原生类型提供原子性保证,从而表现为“relaxed atomic”。并且,所有被声明为顺序一致性的变量可以在整个程序中保持性质(除非在Java中使用sun.misc.unsafe)。尽管这个细微的差异可能看起来并不重要,但是当我们的目标是从同步或是并发算法中挖掘最大性能时,就需要关注这个差异了。

假设你想要在不同语言中声明一个原子整型,下面是你可能会做的:


01C11 (can be relaxed or sequentially consistent)
02 
03 _Atomic int x;
04 
05C++11 or C++14 (can be relaxed or sequentially consistent)
06 
07 atomic<int> x;
08 
09Java (sequentially consistent)
10 
11 volatile int x;

当具备上述信息时&#xff0c;我们可以写出一个简单的Ticket Lock C11版本的实现&#xff0c;如下所示&#xff1a;


01 typedef struct
02 
03{
04 
05     _Atomic long ingress;
06 
07     char padding[64];      // To avoid false sharing between ingress and egress
08 
09     _Atomic long egress;
10 
11} ticket_mutex_t;
12 
13   
14 
15 void ticket_mutex_lock(ticket_mutex_t * self)
16 
17{
18 
19     long lingress &#61; atomic_fetch_add(&self->ingress, 1);
20 
21     while (lingress !&#61; atomic_load(&self->egress)) {
22 
23         sched_yield();
24 
25     }
26 
27     // This thread has acquired the lock on the mutex
28 
29}
30 
31   
32 
33 void ticket_mutex_unlock(ticket_mutex_t * self)
34 
35{
36 
37     atomic_fetch_add(&self->egress, 1);
38 
39}

 

这里有3处不明显的优化&#xff0c;我们可以在上述代码的基础上利用releaxed atomics实现。让我们一个一个看。

  1. 在lock方法中&#xff0c;当egress变量第一次被读取的时候&#xff0c;我们可以用relaxed load优化。

代码如下&#xff1a;


01 void ticket_mutex_lock(ticket_mutex_t * self)
02 
03{
04 
05 long lingress &#61; atomic_fetch_add(&self->ingress, 1);
06 
07// If the ingress and egress match, then the lock as been acquired and
08 
09// we don&#39;t even need to do an acquire-barrier.
10 
11 if (lingress &#61;&#61; atomic_load_explicit(&self->egress, memory_order_relaxed)) return ;
12 
13 while (lingress !&#61; atomic_load(&self->egress)) {
14 
15 sched_yield();  // Replace this with thrd_yield() if you use
16 
17}
18 
19// This thread has acquired the lock on the mutex
20 
21}

我们这样做的原因是atmoic_fetch_add(ingress)将会获取一个barrier&#xff0c;这代表着atomic_load_explicit(egress, memory_order_relaxed)将永远不能在atomic_fetch_add()之前被执行&#xff0c;但是它可以获得一个最新的egress的值&#xff0c;至少在atomic_fetch_add(ingress)执行之后。这意味着egress变量的值将永远不会比atomic_fetch_add(ingress)所返回的值更大。

在x86架构中&#xff0c;这个优化并不会带来效率提升&#xff0c;因为获得屏障是没有代价的&#xff0c;这代表节省一个屏障并不能带来任何收益。但是在其他架构中——如ARMv8&#xff0c;这个优化将带来少量的收益。当我可以在ARMv8上开发&#xff0c;并且配有支持C11/C&#43;&#43;11的gcc时&#xff0c;我会详细说明。

 

  1. 在unlock()方法中&#xff0c;我们可以使用atomic_load()和atomic_store()来替代atomic_fetch_add()。

unlock()方法中并没有在一个原子操作中同时读/写的需求&#xff0c;因为同一时间只有一个线程可以调用这个方法。这说明我们可以这样实现egress变量的增加操作&#xff1a;使用atomic_load()读取egress变量的当前值&#xff0c;然后使用atomic_store()操作将当前值加1写入egress变量。

 

  1. 在前面提到的atomic_load()操作可以是relaxed。

步骤2可以通过relaxed atomic_load()进一步优化。在lock()方法中&#xff0c;atomic_fetch_add()有一个获取屏障的操作&#xff0c;或者在糟糕的情况下&#xff0c;这个屏障在while循环中的atomic_load()&#xff0c;用来保证当前线程获得最新的egress值。我们可以保证在这个屏障和unlock()之间没有其他线程调整egress变量&#xff0c;所以这样做是安全的。

最后的代码如下&#xff1a;


1 void ticket_mutex_unlock( ticket_mutex_t * self)
2 
3{
4 
5 long legress &#61; atomic_load_explicit(&self-> egress, memory_order_relaxed);
6 
7atomic_store(&self-> egress, legress&#43;1);
8 
9}

上述是一些关于如何使用relaxed atomics来优化代码的例子。

现在&#xff0c;我们可以实际地说这些特殊优化所带来的提升是非常小的&#xff0c;并且一些优化是令人费解的&#xff0c;更是难以证明其正确性的。有利的一点是这个代码仍然是跨平台的。你可以在x86&#xff0c;ppc&#xff0c;mips&#xff0c;arm上运行它&#xff0c;因为内存模型的一致性&#xff0c;你有信心保证代码仍然是安全的。

这是我喜爱C11/C&#43;&#43;1x内存模型的原因。

可以在github上找到C11 Ticket Lock源代码&#xff1a;

https://github.com/pramalhe/ConcurrencyFreaks/blob/master/C11/locks/ticket_mutex.h

https://github.com/pramalhe/ConcurrencyFreaks/blob/master/C11/locks/ticket_mutex.c



推荐阅读
  • 微软头条实习生分享深度学习自学指南
    本文介绍了一位微软头条实习生自学深度学习的经验分享,包括学习资源推荐、重要基础知识的学习要点等。作者强调了学好Python和数学基础的重要性,并提供了一些建议。 ... [详细]
  • 云原生边缘计算之KubeEdge简介及功能特点
    本文介绍了云原生边缘计算中的KubeEdge系统,该系统是一个开源系统,用于将容器化应用程序编排功能扩展到Edge的主机。它基于Kubernetes构建,并为网络应用程序提供基础架构支持。同时,KubeEdge具有离线模式、基于Kubernetes的节点、群集、应用程序和设备管理、资源优化等特点。此外,KubeEdge还支持跨平台工作,在私有、公共和混合云中都可以运行。同时,KubeEdge还提供数据管理和数据分析管道引擎的支持。最后,本文还介绍了KubeEdge系统生成证书的方法。 ... [详细]
  • Gitlab接入公司内部单点登录的安装和配置教程
    本文介绍了如何将公司内部的Gitlab系统接入单点登录服务,并提供了安装和配置的详细教程。通过使用oauth2协议,将原有的各子系统的独立登录统一迁移至单点登录。文章包括Gitlab的安装环境、版本号、编辑配置文件的步骤,并解决了在迁移过程中可能遇到的问题。 ... [详细]
  • Apache Shiro 身份验证绕过漏洞 (CVE202011989) 详细解析及防范措施
    本文详细解析了Apache Shiro 身份验证绕过漏洞 (CVE202011989) 的原理和影响,并提供了相应的防范措施。Apache Shiro 是一个强大且易用的Java安全框架,常用于执行身份验证、授权、密码和会话管理。在Apache Shiro 1.5.3之前的版本中,与Spring控制器一起使用时,存在特制请求可能导致身份验证绕过的漏洞。本文还介绍了该漏洞的具体细节,并给出了防范该漏洞的建议措施。 ... [详细]
  • 本文介绍了设计师伊振华受邀参与沈阳市智慧城市运行管理中心项目的整体设计,并以数字赋能和创新驱动高质量发展的理念,建设了集成、智慧、高效的一体化城市综合管理平台,促进了城市的数字化转型。该中心被称为当代城市的智能心脏,为沈阳市的智慧城市建设做出了重要贡献。 ... [详细]
  • 使用在线工具jsonschema2pojo根据json生成java对象
    本文介绍了使用在线工具jsonschema2pojo根据json生成java对象的方法。通过该工具,用户只需将json字符串复制到输入框中,即可自动将其转换成java对象。该工具还能解析列表式的json数据,并将嵌套在内层的对象也解析出来。本文以请求github的api为例,展示了使用该工具的步骤和效果。 ... [详细]
  • FeatureRequestIsyourfeaturerequestrelatedtoaproblem?Please ... [详细]
  • 先看官方文档TheJavaTutorialshavebeenwrittenforJDK8.Examplesandpracticesdescribedinthispagedontta ... [详细]
  • 海马s5近光灯能否直接更换为H7?
    本文主要介绍了海马s5车型的近光灯是否可以直接更换为H7灯泡,并提供了完整的教程下载地址。此外,还详细讲解了DSP功能函数中的数据拷贝、数据填充和浮点数转换为定点数的相关内容。 ... [详细]
  • Spring常用注解(绝对经典),全靠这份Java知识点PDF大全
    本文介绍了Spring常用注解和注入bean的注解,包括@Bean、@Autowired、@Inject等,同时提供了一个Java知识点PDF大全的资源链接。其中详细介绍了ColorFactoryBean的使用,以及@Autowired和@Inject的区别和用法。此外,还提到了@Required属性的配置和使用。 ... [详细]
  • 本文介绍了一种轻巧方便的工具——集算器,通过使用集算器可以将文本日志变成结构化数据,然后可以使用SQL式查询。集算器利用集算语言的优点,将日志内容结构化为数据表结构,SPL支持直接对结构化的文件进行SQL查询,不再需要安装配置第三方数据库软件。本文还详细介绍了具体的实施过程。 ... [详细]
  • 本文介绍了Codeforces Round #321 (Div. 2)比赛中的问题Kefa and Dishes,通过状压和spfa算法解决了这个问题。给定一个有向图,求在不超过m步的情况下,能获得的最大权值和。点不能重复走。文章详细介绍了问题的题意、解题思路和代码实现。 ... [详细]
  • 从零基础到精通的前台学习路线
    随着互联网的发展,前台开发工程师成为市场上非常抢手的人才。本文介绍了从零基础到精通前台开发的学习路线,包括学习HTML、CSS、JavaScript等基础知识和常用工具的使用。通过循序渐进的学习,可以掌握前台开发的基本技能,并有能力找到一份月薪8000以上的工作。 ... [详细]
  • 设计模式——模板方法模式的应用和优缺点
    本文介绍了设计模式中的模板方法模式,包括其定义、应用、优点、缺点和使用场景。模板方法模式是一种基于继承的代码复用技术,通过将复杂流程的实现步骤封装在基本方法中,并在抽象父类中定义模板方法的执行次序,子类可以覆盖某些步骤,实现相同的算法框架的不同功能。该模式在软件开发中具有广泛的应用价值。 ... [详细]
  • 本文介绍了在go语言中利用(*interface{})(nil)传递参数类型的原理及应用。通过分析Martini框架中的injector类型的声明,解释了values映射表的作用以及parent Injector的含义。同时,讨论了该技术在实际开发中的应用场景。 ... [详细]
author-avatar
lee某某
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有