CPU的并发控制

简介: CPU的并发控制

缓存一致性协议


在网上的很多文章,都把缓存一致性协议叫做MESI,其实不单单指MESI,MESI只是因特尔CPU的一种协议.


MESI指的其实只是4种状态:缓存被修改状态,独占状态,分享状态,失效状态.


其他缓存一致性协议还有MOSI,MSI.缓存锁,总线锁。


比如Java中volitale就是使用的缓存锁,调用lock指令锁住总线.只要一修改,锁住的这部分数据,那么原数据相关的缓存都会自动失效,然后获取到最新修改后的值.


到后期,一般都不用总线锁了,锁总线期间其他CPU没法访问内存,后面已经都优化成了缓存锁 + 总线索的方式,因为有些缓存无法使用缓存锁来锁定。


缓存行


cpu的速度比内存快的多,大概100比1左右.也就是在超线程期间,访问寄存器的速度比访问外界内存的速度快100倍,在这里指的是访问速度.


如何让访问速度变快呢? 最好的方法就是建立缓存,cpu优先去缓存当中读取数据,如果没有再去内存当中读取数据,然后将数据放入缓存当中.


现在一般都是使用三级缓存,这是硬件与性能平衡之后的结果.


性能时间


寄存器 小于1纳秒.


1级缓存 1纳秒左右.


2级缓存 3纳秒左右


3级缓存 5纳秒最优.


主内存 80纳秒.


image.png


重点:没有什么架构是加一层解决不了的,比如说加了一层JVM,就实现了跨操作系统的功能.


当我们读取数据的时候,如果单单只读取一个数据,然后再放入内存当中,此时的效率是很慢的,我们如果需要其他数据了就需要循环往复的读取,在这里我们CPU做了一个优化,他会一次性读取一行的数据,从而避免多次读取操作而降低CPU的性能.


这个数据量太大太小都不好,一个找的慢,一个从内存中读取太大也不好,在三级缓存的应用环境下,最佳大小是64个字节.


当我们明白了CPU读取数据是一行一行读取的,以及缓存行一行大小是64个字节之后.


我们需要保证的是,如何让多核之间保证数据的一致性,这里有个协议就叫做缓存一致性协议.


对这个协议在Java代码层面有优化的实例,disruptor,LinkedBlockingQueue源码就有缓存行优化的概念,定义了多个变量,变量大小大于64byte,那么就减少了共享数据互相通知的时间.


比如disruptor单机版最快MQ,他是一个环形缓冲区,是一个首尾相连的数组,只有一个指针,放完一个消息往下挪一格,如果满了那么使用CAS自旋,访问时候查询一下当前数组是否存在数据,自旋等待.


image.png


缓存行优化


在代码中我们也可以利用这一特性进行缓存行的优化


image.png


或者是在JDK1.8中使用@Contended注解 并且需要在JVM启动中设置-XX:-RestrictContended取消限制,不然会导致无法生效的问题出现,注意在这里我特地去百度查询过这个注解只有在JDK1.8中才能生效,1.7和1.9好像都是不能使用的,也是一个很奇怪的例子。


总线锁


也是CAS底层原理,当CAS对值进行修改的时候会调用c++的代码,使用硬件级别的总线锁死其他的cpu,在这段修改期间内,不允许其他CPU执行操作.


缓存锁


总线锁的升级版本,会通过修改数据的4种状态来选择对应的操作.


系统屏障


编译器和CPU会在不影响结果(这儿主要是根据数据依赖性)的情况下对指令重排序,使性能得到优化,但是实际情况里面有些指令虽然没有前后依赖关系,但是重排序之后影响到输出结果,这时候可以插入一个内存屏障,相当于告诉CPU和编译器限于这个命令的必须先执行,后于这个命令的必须后执行。


内存屏障的另一个作用是强制更新一次不同CPU的缓存,这意味着如果你对一个volatile字段进行写操作,你必须知道:


  1. 一旦你完成写入,任何访问这个字段的线程将会得到最新的值;
  2. 在你写入之前,会保证所有之前发生的事已经发生,并且任何更新过的数据值也是可见的,因为内存屏障会把之前的写入值都刷新到缓存。


CPU的乱序执行


CPU分为IO密集型和CPU密集型,当CPU的A线程是IO线程,正在等待内存数据的时候,那么在CPU当中会做一个优化,CPU会先让B线程进行执行,最后执行顺序的结果可能是213,但是并不会妨碍最终数据的一致性,但是当多线程的时候,是有可能出现很致命的错误的.


举个最典型的案例,Double Check Lock单例模式,

publicclassSingleton {
privatestaticSingletonsingleton;
//private volatile static Singleton singleton;privateSingleton() {}
publicSingletongetInstance() {
if (singleton==null) {
synchronized (Singleton.class){
if(singleton==null) {
singleton=newSingleton();
                }
            }
        }
returnsingleton;
    }
}


上述代码虽然使用了双重锁检查尽可能的规避掉了效率上的影响,但是仍然可能会返回半初始化的对象,所以要禁止指令重排序,通过使用volatile关键字的第二种功能。


实例化一个对象要分为三个步骤:


  1. 分配内存空间
  2. 初始化对象
  3. 将内存空间的地址赋值给对应的引用


但是在上图中没有禁止指令重排序的执行顺序可能是231,那么就会返回一个残缺的对象,出现致命的错误.


硬件层面原理


lock总线锁命令调用全内存屏障,其中包含了读写屏障,所以既能保证读取最新的值,又能保证禁止指令重排的功能.


syn的硬件层面也和Volitale类似 都是调用的Lock指令,区别是在JVM中,syn是通过C++调用的同步互斥区来实现的.


HOTSPOT对应源码


HOTSPOT源码执行过程


image.png


x86CPU lock指令会调用全屏障

image.png

目录
相关文章
|
4月前
|
开发框架 并行计算 算法
揭秘Python并发神器:IO密集型与CPU密集型任务的异步革命,你竟还傻傻分不清?
揭秘Python并发神器:IO密集型与CPU密集型任务的异步革命,你竟还傻傻分不清?
56 4
|
3月前
|
监控 并行计算 数据处理
构建高效Python应用:并发与异步编程的实战秘籍,IO与CPU密集型任务一网打尽!
在Python编程的征途中,面对日益增长的性能需求,如何构建高效的应用成为了每位开发者必须面对的课题。并发与异步编程作为提升程序性能的两大法宝,在处理IO密集型与CPU密集型任务时展现出了巨大的潜力。今天,我们将深入探讨这些技术的最佳实践,助你打造高效Python应用。
48 0
|
3月前
|
开发框架 并行计算 .NET
脑洞大开!Python并发与异步编程的哲学思考:IO密集型与CPU密集型任务的智慧选择!
脑洞大开!Python并发与异步编程的哲学思考:IO密集型与CPU密集型任务的智慧选择!
36 1
|
4月前
|
算法 Java 程序员
解锁Python高效之道:并发与异步在IO与CPU密集型任务中的精准打击策略!
在数据驱动时代,高效处理大规模数据和高并发请求至关重要。Python凭借其优雅的语法和强大的库支持,成为开发者首选。本文将介绍Python中的并发与异步编程,涵盖并发与异步的基本概念、IO密集型任务的并发策略、CPU密集型任务的并发策略以及异步IO的应用。通过具体示例,展示如何使用`concurrent.futures`、`asyncio`和`multiprocessing`等库提升程序性能,帮助开发者构建高效、可扩展的应用程序。
168 0
|
6月前
|
并行计算 监控 数据处理
构建高效Python应用:并发与异步编程的实战秘籍,IO与CPU密集型任务一网打尽!
【7月更文挑战第16天】Python并发异步提升性能:使用`asyncio`处理IO密集型任务,如网络请求,借助事件循环实现非阻塞;`multiprocessing`模块用于CPU密集型任务,绕过GIL进行并行计算。通过任务类型识别、任务分割、避免共享状态、利用现代库和性能调优,实现高效编程。示例代码展示异步HTTP请求和多进程数据处理。
71 8
|
6月前
|
算法 Java 程序员
解锁Python高效之道:并发与异步在IO与CPU密集型任务中的精准打击策略!
【7月更文挑战第17天】在数据驱动时代,Python凭借其优雅语法和强大库支持成为并发处理大规模数据的首选。并发与异步编程是关键,包括多线程、多进程和异步IO。对于IO密集型任务,如网络请求,可使用`concurrent.futures`和`asyncio`;CPU密集型任务则推荐多进程,如`multiprocessing`;`asyncio`适用于混合任务,实现等待IO时执行CPU任务。通过这些工具,开发者能有效优化资源,提升系统性能。
109 4
|
6月前
|
分布式计算 并行计算 Java
Python并发风暴来袭!IO密集型与CPU密集型任务并发策略大比拼,你站哪队?
【7月更文挑战第17天】Python并发处理IO密集型(如网络请求)与CPU密集型(如数学计算)任务。IO密集型适合多线程和异步IO,如`ThreadPoolExecutor`进行网页下载;CPU密集型推荐多进程,如`multiprocessing`模块进行并行计算。选择取决于任务类型,理解任务特性是关键,以实现最佳效率。
103 4
|
6月前
|
开发框架 并行计算 .NET
脑洞大开!Python并发与异步编程的哲学思考:IO密集型与CPU密集型任务的智慧选择!
【7月更文挑战第18天】在Python中,异步编程(如`asyncio`)适合处理IO密集型任务,通过非阻塞操作提高响应性,例如使用`aiohttp`进行异步HTTP请求。而对于CPU密集型任务,由于GIL的存在,多进程(`multiprocessing`)能实现并行计算,如使用进程池进行大量计算。明智选择并发模型是性能优化的关键,体现了对任务特性和编程哲学的深刻理解。
55 2
|
6月前
|
开发框架 并行计算 算法
揭秘Python并发神器:IO密集型与CPU密集型任务的异步革命,你竟还傻傻分不清?
【7月更文挑战第18天】Python并发编程中,异步IO适合IO密集型任务,如异步HTTP请求,利用`asyncio`和`aiohttp`实现并发抓取,避免等待延迟。而对于CPU密集型任务,如并行计算斐波那契数列,多进程通过`multiprocessing`库能绕过GIL限制实现并行计算。选择正确的并发模型能显著提升性能。
99 2
|
6月前
|
开发框架 数据挖掘 .NET
显微镜下的Python并发:细说IO与CPU密集型任务的异步差异,助你精准施策!
【7月更文挑战第16天】在Python并发编程中,理解和区分IO密集型与CPU密集型任务至关重要。IO密集型任务(如网络请求)适合使用异步编程(如`asyncio`),以利用等待时间执行其他任务,提高效率。CPU密集型任务(如计算)则推荐使用多进程(如`multiprocessing`),绕过GIL限制,利用多核CPU。正确选择并发策略能优化应用性能。
90 2

相关实验场景

更多