无锁编程与有锁编程的性能对比与分析

高性能编程-无锁化编程总结 主要描述高性能编程及无锁化编程的一些方法及思想,为广大程序员们提供一些高性能编程的思路。无锁化编程大概的思路就是将各类锁方案替换成状态机机制方案。 阅读详情

 

      最近维护的一个网络服务器遇到性能问题,于是就对原有的程序进行了较大的框架改动。改动最多的是线程工作模式与数据传递方式,最终的结果是改变锁的使用模式。经过一番改进,基本上可以做到 GMb 网卡全速工作处理。在 性能达标之后,一度在想有没有什么办法使用更加轻量级锁,或者去掉锁的使用,为此搜索一些相关的研究成果,并做了一些实验来验证这些成果,因而就有这篇文章。希望有做类似工作的同行可以有所借鉴。如果有人也有相关的经验,欢迎和我交流。

 

1 无锁编程概述

      本节主要对文献 [1] 进行概括,做一些基础知识的介绍。

      所谓有锁编程,就是当你需要共享数据的时候,你需要有序的去访问,所有改变共享数据的操作都必须表现出原子的语义,即便是像 ++k ,这种操作也需要使用锁进行。有锁编程面临时效率的下降、死锁、优先级反转等问题,都需要设计者小心的进行优化和解决。本文并不对这三个问题进行讨论。

      在无锁编程中,并不是说所有操作都是原子的,只有一个很有限的操作集是原子的,这就意味着无锁编程十分困难。那么这个有限的操作集是否存在,存在的话包含哪些原子操作呢? 2003 年 Maurice Herlihy 的一篇论文 ”Wait-Free Synchronization”[3] 解决了这个问题。这里给出文章的结论,文章指出像 test-and-set,swap,fetch-and-add 甚至是原子队列对于多线程而言都无法做到 lock-free 。而最朴素最简单的原语 CAS(compare-and-swap) 操作即可以完成所有的无锁功能,其他的如 LL/SC (load linked/store conditional) 。 CAS 的伪码如下:

 

      CAS 将 expected 与一个内存地址进行比较,如果比较成功,就将内存内容替换为 new 。当前大多数机器都在硬件级实现了这个操作,在 Inter 处理器上这个操作是 CMPXCHG ,因而 CAS 是一个最基础的原子操作。

      wait-free / lock-free 与 有锁对比

      wait-free 的过程可以通过有限步骤完成,而不管其他线程的速度。

      lock-free 的过程保证至少一个线程在执行,其他线程可能会被延迟,但系统整体仍在前进。

      有锁的情况下,如果某个线程占有锁,则其他线程就无法执行。更普通的,有锁需要避免死锁和活锁的情况。  

2 无锁编程的相关研究与进展

        本节内容对文献 [2] 进行概述,介绍当前已经实现的无锁算法与数据结构。

        近二十年来研究者们对 lock-free 和 wait-free 的算法和数据结构进行了大量的研究。实现了一些 wait-free 和 lock-free 的算法,比如 FIFO 的队列和 LIFO 的栈,而更复杂的优化级队列、 hash 表及红黑树的 lock-free 算法也渐渐为人所知。

        无锁算法的实现都依赖内存屏障,因而具有平台相关性。下面将列举目前已经较为成熟的原子操作和算法数据结构的实现。

  • MidiShare Source Code is available under the GPL license. MidiShare includes implementations of lock-free FIFO queues and LIFO stacks.
  • Appcore is an SMP and HyperThread friendly library which uses Lock-free techniques to implement stacks, queues, linked lists and other useful data structures. Appcore appears currently to be for x86 computers running Windows. The licensing terms of Appcore are extremely unclear.
  • Noble – a library of non-blocking synchronisation protocols. Implements lock-free stack, queue, singly linked list, snapshots and registers. Noble is distributed under a license which only permits non-commercial academic use.
  • lock-free-lib published under the GPL license. Includes implementations of software transactional memory, multi-workd CAS primitives, skip lists, binary search trees, and red-black trees. For Alpha, Mips, ia64, x86, PPC, and Sparc.
  • Nonblocking multiprocessor/multithread algorithms in C++ (for MSVC/x86) posted by Joshua Scholar to musicdsp.org , and are presumably in the public domain. Included are queue, stack, reference-counted garbage collection, memory allocation, templates for atomic algorithms and types. This code is largely untested. A local mirror is here .
  • Qprof includes the Atomic_ops library of atomic operations and data structures under an MIT-style license. Only available for Linux at the moment, but there are plans to support other platforms. download available here
  • Amino Concurrent Building Blocks provides lock free datastructures and STM for C++ and Java under an Apache Software (2.0) licence.

      其中 Noble 已经进行了商业化, License 相当不便宜。

3 性能分析

      本节对 PTHREAD 中的 mutex , windows 中的原子增,及 CAS 原子操作进行对比,并对 MidiShare 中实现的无锁 FIFO 队列与基于 STL 的 list 实现的有锁队列进行的性能对比和分析,并对优化方式进行了总结。

3.1 原子增的性能测试

      测试机 CPU 为 Intel E5300 2.60GHZ

      首先是对简单的递增操作进行了测试,分别对无任何同步机制的 ++ 操作、 pthread_mutex 保护的 ++ 操作,以及 CAS 的语义实现的 atomic_add1() 以及 windows 下的 interlockedIncrease() 进行了单个线程情况下的定量测试。  

i++

3.2 亿

lock(p_mutex);i++;unlock(p_mutex);

2 千万

CAS_atomic_add1(i)

4 千万

interlockedIncrease(&i)

4 千万

       首先在无任何同步情况下, CPU 可以每秒执行 ++ 操作 3.2 亿次,接近于 CPU 的主频速率。而每次 ++ 时执行 thread_mutex_lock() 及 unlock() 操作情况下, CPU 每秒只能执行 2 千万次,这就是说 CPU 每秒钟可以执行加锁及解锁操作共 4 千万次,加解锁的开销是执行加法指令的的 15 倍左右。而 CAS 的情况稍好,为每秒 4 千万次。这个速度与 windows 下的 interlockedIncrease() 的执行速度十分近似。

      从上面的测试结果来看, windows 下的原子增操作与 CAS 实现的增操作代价基本是相同的,估计 windows 底层也是借助汇编指令 CMPXCHG 的 CAS 来实现原子增操作的。当然 pthread_mutex 作为一种互斥锁,也是拥有相当高的效率的,在没有锁突然的情况下,加锁开销与一次 CAS 的开销相当。

      但如果对比无同步的 ++ 操作,硬件级的同步也造成了至少 8 倍的性能下降。

      接着,对 pthread_mutex 的程序进行了逻辑优化,分别测试了 ++ 执行 8 次、 20,100 次进行一次加解锁的情况。

lock();for(k=0;k<8;i++,k++);unlock()

1.2 亿

lock();for(k=0;k<20;i++,k++);unlock()

2 亿

lock();for(k=0;k<100;i++,k++);unlock()

3.4 亿

      结果 CPU 每秒钟可以执行 ++ 的次数为 1.2 亿 /2 亿 /3.4 亿,这种情况与预期是一致的,因为每秒钟调用加解锁的次数分别是原来的 1/8 、 1/20 和 1/100 ,当执行 100 次 ++ 进行一次加解锁后,性能已经达到了无任何同步时的性能。当然原子的 interlockedIncrease() 和 CAS 实现的 atomic_add1() 都不具备这种批量处理的改进优势,无论如果,它们最好的执行情况已经固定了。

对于在单线程与多线程的情况下的 windows 下的原子操作的性能测试情况,可以参考文献 [4] ,这里只列出其中的结论。其所列的测试机 CPU 为 Intel2.66GHZ 双核处理器。

      单个线程执行 2 百万次原子增操作

interlockedIncrease

78ms

Windows CriticalSection

172ms

OpenMP 的 lock 操作

250ms

      两个线程对共享变量执行 2 百万次原子增操作

interlockedIncrease

156ms

Windows CriticalSection

3156ms

OpenMP 的 lock 操作

1063ms

3.2 无锁队列与有锁队列的性能测试

      这里测试的无锁列队由 MidiShare 实现的,而有锁队列是通过 pthread_mutex 与 c++ 的 STL list 共同实现。这里只列出测试结果。

      对于存储相同的数据的情况下,从主线程 enque 并从子线程 deque ,计算每秒钟 enque/deque 的次数,当然二者基本上是相同的。

      无锁队列的性能在 150w -200w 次入队操作,这个性能已经无法再有任何提高,因为每次入队出队操作都是硬件级的互斥。而对于有锁队列,根据每次加解锁之间处理入队的次数的不同,有以下的结果:

lock();for(k=0;k<x;i++,k++);unlock()

结果(次/s)

x=1

40 万

x=10

190 万

x=128

350 万

x=1000

400 万

x=10000

396 万

      这说明通过对锁之间的数据进行批处理,可以极大的提高系统的性能,而使用原子操作,则无法实现批处理上的改进。

4 结论

      通过上面的无锁和有锁的性能测试,可以得出这样的结论,对于 CAS 实现的硬件级的互斥,其单次操作性能比相同条件下的应用层的较为高效,但当多个线程并发时,硬件级的互斥引入的代价与应用层的锁争用同样令人惋惜。因此如果纯粹希望通过使用 CAS 无锁算法及相关数据结构而带来程序性能的大量提升是不可能的,硬件级原子操作使应用层操作变慢,而且无法再度优化。相反通过对有锁多线程程序的良好设计,可以使程序性能没有任何下降,可以实现高度的并发性。

      但是我们也要看到应用层无锁的好处,比如不需要程序员再去考虑死锁、优先级反转等棘手的问题,因此在对应用程序不太复杂,而对性能要求稍高时,可以采用有锁多线程。而程序较为复杂,性能要求满足使用的情况下,可以使用应用级无锁算法。

      至于如何对多线程的工作模式进行更好的调度,可以参考文献 [5] ,文献介绍了一种较好的线程间合作的工作模式,当然前提是机器的处理器个数较多,足以支持多组线程并行的工作。如果处理器个数较,较多的线程之间在各个核心上来回调度增加了系统上下文切换的开销,会导致系统整体性能下降。

 

参考文献

[1] Lock-Free Data Structures http://www.drdobbs.com/184401865

[2] Some notes on lock-free wait-free algorithms http://www.rossbencina.com/code/lockfree

[3] Wait-Free Synchronization http://www.podc.org/dijkstra/2003.html

[4] OpenMP 创建线程中的锁及原子操作性能比较 http://blog.163.com/kangtao-520/blog/static/772561452009510751068/

[5] 多核编程中的线程分组竞争模式 http://kangtao-520.blog.163.com/blog/static/77256145200951074121305/

 

cesium加载天地图、OSM、谷歌底图 以上示例是加载矢量底图,若要加载影像或者其他只需要更换相应的url,在WebMapTileServiceImageryProvider中还要更改layer参数。url中’‘vec_w’,一些其他风格,基于CRAT渲染,两种方法效果差别不大。有两种方法加载天地图分别是。对应投影样式,具体可看。还要谷歌的一些卫星底图。 阅读详情

相关推荐

CTF实战:手把手教你用Python破解转轮机加密(附完整脚本)

本文详细介绍了如何使用Python逆向破解转轮机加密,特别针对CTF比赛中的Crypto类题目。通过解析转轮机加密原理、实战Python脚本编写及调试技巧,帮助参赛者快速掌握破解方法,提升在攻防世界等CTF比赛中的解题效率。

silver的专栏 1116

有锁队列、无锁队列

【代码】有锁队列、无锁队列。

weixin_44585214的博客 1532

阿里云导出的镜像raw转换成vmdk格式工具

阿里云导出的镜像raw转换成vmdk格式工具 1.安装java环境,安装教程不介绍了 2.raw2vmdk软件下载: 在我的链接里面: 3.使用 打开cmd.bat软件: java -jar raw2vmdk.jar (原镜像)C:\\images\\raw\\disk.img (转换的镜像)D:\\images\\vmdk\\disk.vmdk 4.vmware打开vmdk文件 ...

3843

深入理解无锁编程

hi,大伙好,今天介绍一下无锁编程基础知识,希望大家可以了解无锁编程基本原理。无锁编程是一个挑战,不仅因为任务本身的复杂性,还因为从一开始就很难深入了解这个主题,因为该主题和底层技术(编译...

极客重生 1961

Obsidian和Typora图片路径同步终极指南:告别混乱的assets文件夹

本文详细解析了Obsidian与Typora图片路径同步的终极解决方案,通过Custom Attachment Location插件实现动态路径管理,解决了两款工具间图片兼容性问题。文章提供了从基础配置到高级应用的完整指南,帮助用户告别混乱的assets文件夹,提升工作效率。

g9h0i1的博客 821

强化学习_06_pytorch-TD3实践(BipedalWalkerHardcore-v3)

DDPG的critic会高估, 从而导致actor策略失败。TD3是增加了三个关键技巧优化DDPG。经过优化后的TD3(Twin Dalayed DDPG 双延迟深度确定性策略梯度算法)适合于具有高维连续动作空间的任务。

RL小站 2689

linux无锁队列性能对比,无锁队列的分析与设计

取而代之”执行,说白了就是不要因为一条鱼腥了一锅汤,让应用的整体的执行效率得到提高;但是多线程编程是一柄双刃剑,因为当多线程需要共享数据和通信的时候,事情就会变得非常麻烦,会发生诸如死锁,非控制的共享数据存取,内存动态分配与释放等等问题。即便你运气好到没有遇到这些问题,还是有诸如Cache失效率过高等应用性能的掣肘(假设你对自己程序的性能很在意),因此无锁数据结构就应运而生了。考虑到队列广泛的应用...

weixin_29560063的博客 390

如何绘制电气绝缘图(GB9706.1-2007和GB9706.1-2020差异)

是用来描述要考虑设备的绝缘状况和它们的值 ,从而便于和相关部门交流和沟通,如权威机构和检测实验室等。本文为绝缘图的实例, 包括所有相关的绝缘路径以及介电强度,爬电距离和电气间隙的值。

学海无涯的专栏 1万+

无锁队列与有锁队列性能比较

最近研究boost的无锁队列,测试了一下性能,发现无锁队列比有锁的还要慢 testqueue.cpp #include <time.h> #include <boost/thread/thread.hpp> #include <boost/lockfree/queue.hpp> #include <iostream> #include <boost/atomic.hpp> boost::atomic_int producer_count(0

XiaoWhy的专栏 2603

(全网最详细教程)康耐视读码器(以DM260S为例子)详细实用步骤

(全网最详细教程)康耐视读码器(以DM260S为例子)详细实用步骤

视觉人机器视觉的博客 7464

Playwright Test Agent 实战:零代码基础也能写自动化测试「喂饭教程」

本文详细介绍了Playwright智能体的三大核心组件:规划智能体设计测试策略,生成智能体转换用例为代码,修复智能体自动修正失败测试。教程包含环境配置步骤、智能体使用方法和实战案例,展示了如何通过自然语言指令快速创建稳定的测试脚本。相比传统方法,Playwright智能体大幅降低测试自动化门槛,提升效率并减少维护成本,使测试人员无需编码基础也能实现高质量的自动化测试。

blues_C的博客 2018

并发编程原理与实战(二十三)StampedLock应用实战与各种锁性能对比分析

本文通过官方提供的多线程更新坐标并计算到原点的距离这个例子,说明了StampedLock的应用,然后通过设计不同的读写线程,分析读写坐标时的数据一致性,最后通过用ReentrantLock和synchronized分别对读取坐标进行加锁,对比了与StampedLock 乐观锁实现的耗时。通过该例子,进一步加深了对StampedLock 的理解。

真战 1286

【ComfyUI】FluxKontext 人物服装平铺重构

本文介绍了一个基于ComfyUI的高保真服装语义提取工作流,利用FluxKontext模型实现人物服装结构的精准抽离与平铺展示。工作流包含图像输入、潜空间处理、语义引导和生成输出四个阶段,通过Flux_Kontext_dev_fp8主模型、双CLIP文本编码和VAE转换等核心组件,配合FluxKontextImageScale、ReferenceLatent等节点完成服装结构的稳定提取。流程中CLIPTextEncode负责语义解析,FluxGuidance强化引导力度,KSampler执行最终生成,最终输

Mr数据杨 956

无锁编程和有锁编程效率对比

最近维护的一个网络服务器遇到性能问题,于是就对原有的程序进行了较大的框架改动。改动最多的是线程工作模式与数据传递方式,最终的结果是改变锁的使用模式。经过一番改进,基本上可以做到 GMb 网卡全速工作处理。在 性能达标之后,一度在想有没有什么办法使用更加轻量级锁,或者去掉锁的使用,为此搜索一些相关的研究成果,并做了一些实验来验证这些成果,因而就有这篇文章。希望有做类似工作的同行可以有所借鉴。如果有人

cws1214的专栏 6186

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

CAS原子操作实现无锁及性能分析

原文地址:http://blog.youkuaiyun.com/chen19870707/article/details/41083183 CAS原子操作实现无锁及性能分析   Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blog.youkuaiyun.com/chen19870707 Date:Nov

u011244446的专栏 3545

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

C++中的无锁编程

无锁编程作为一种高级并发编程技术,能够在高并发场景下提供显著的性能优势。C++11及后续标准通过提供原子操作和内存序模型,为无锁编程提供了坚实的基础。然而,无锁编程也面临着诸多挑战,包括复杂的内存管理、难以验证的正确性以及潜在的ABA问题。随着硬件和编程语言的发展,我们可以期待更多的工具和库来简化无锁编程。在实际应用中,应当根据具体场景选择合适的并发策略,无锁编程并非适用于所有情况。对于并发度不高或对性能要求不严格的场景,传统的基于锁的同步机制可能是更简单、更可靠的选择。

exlink2012的专栏 1350

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

C++ 无锁编程

无锁编程(Lock-Free Programming)是一种并发编程范式,其核心目标是避免使用传统互斥锁(如mutex),通过原子操作和内存顺序保证线程安全。它要求至少一个线程能够在有限步内完成操作,从而避免死锁和优先级反转等问题。与基于锁的编程相比,无锁设计通常能提高并发性能,尤其在低竞争场景下。无锁编程通过原子操作和精细的内存控制提升并发性能,但代价是更高的复杂性和调试难度。在需要极致性能的场景下合理选择,并借助工具和现有库降低风险。对于多数应用,基于锁的抽象(如std::mutex。

qq_39623515的博客 803

无锁编程:突破性能瓶颈的并发重构

并发重构:打破锁争用的高吞吐革命 本文探讨现代高性能计算中多线程编程的优化策略。锁竞争已成为系统性能的主要瓶颈,导致线程频繁争用资源。文章提出通过并发重构技术解决这一问题,重点介绍了: 锁竞争检测:使用perf工具量化锁等待时间占比 无锁编程:基于std::atomic和CAS操作实现线程安全 内存序优化:平衡性能与正确性的六种内存序策略 无锁队列实现:通过原子操作和循环重试确保线程安全 文章包含详细代码示例,展示如何将粗粒度锁重构为高效的无锁数据结构,同时分析ABA问题解决方案和不同内存序的性能影响。这些

技术分享 1045

深入解析 synchronized 锁升级:从偏向锁到重量级锁的设计哲学

偏向锁是“乐观主义”的极致,假设世界没有竞争。轻量级锁是“有限妥协”,通过自旋尝试化解短暂冲突。重量级锁是“终极防御”,以成本换安全。

younuoqianjin的博客 1001

无锁编程基础

背景 我们处在技术快速发展的时代,竞争变得前所未有的激烈,不仅要十八般武艺俱全,还得选对正确的技术,跟上发展的脚步,并贴上精研某个专业方向的标签。我们不仅要面对多线程和并发,还要考虑多核时代的并行计算,无锁编程或许是一种选择,可能会提升性能,也可能避免锁的使用引起的错误,同时会带来编程习惯的变革。 不可否认,无锁技术是目前各种并发解决方案中比较受争议的一种,尽管它基于最基本的编程技术,不依赖于任何语言和平台,但是这项技术有些诡异,掌握起来颇有难度,有点曲高和寡,所以没有大面积应用在编程中。 技术本身

usstmiracle的博客 674

多核编程中的线程随机竞争模式的概率分析

  多核编程中的线程随机竞争模式的概率分析 前一篇多核编程中的线程分组竞争模式中谈到了让线程分组竞争以解决多核CPU遇到的锁竞争导致的饥饿问题。并不是任意的共享数据都能够设计成进行分组竞争的模式,比如最常用的需要用于查找的数据结构,当数据结构分成多个子数据结构后,每次查找时,不能指定查找某个特定的子数据结构,而必须进行二级查找,先在整个数据结构内找到对应的子数据结构(不加锁),然后

周伟明的多核、测试专栏 1万+

Pthreads并行编程之spin lock与mutex性能对比分析

来自: www.parallellabs.com Pthreads并行编程之spin lock与mutex性能对比分析 POSIX threads(简称Pthreads)是在多核平台上进行并行编程的一套常用的API。线程同步(Thread Synchronization)是并行编程中非常重要的通讯手段,其中最典型的应用就是用Pthreads提供的锁机制(lock)来对多个线程之间共 享的临

ZWFGOGO的专栏 478

你还在用互斥锁?2024年C++无锁编程(Lock-Free)方案全面解析

掌握高效C++并发控制方案,告别性能瓶颈。本文深入解析2024年C++无锁编程核心方法,涵盖原子操作、CAS应用与典型场景,提升多线程程序性能与响应性。无需互斥锁,实现高并发安全通信,值得收藏。

SimTrans的博客 442
上一篇: 我的插件开发学习1--XHTML
下一篇: Linux服务器丢弃window7的SYN导致三次握手无法完成的问题分析
zzulp
博客等级 码龄19年 464粉丝 176原创
评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值