【并发编程】CPU cache结构和缓存一致性(MESI协议)

2023-11-15

一、cache

    cpu cache已经发展到了三级缓存结构,基本上现在买的个人电脑都是L3结构。

1. cache的意义

    为什么需要CPU cache?因为CPU的频率太快了,快到主存跟不上,这样在处理器时钟周期内,CPU常常需要等待主存,浪费资源。所以cache的出现,是为了缓解CPU和内存之间速度的不匹配问题(结构:cpu -> cache -> memory)。

    CPU cache有什么意义?cache的容量远远小于主存,因此出现cache miss在所难免,既然cache不能包含CPU所需要的所有数据,那么cache的存在真的有意义吗?当然是有意义的——局部性原理。

    A. 时间局部性:如果某个数据被访问,那么在不久的将来它很可能被再次访问;

    B. 空间局部性:如果某个数据被访问,那么与它相邻的数据很快也可能被访问;

2. cache和寄存器

    存储器的三个性能指标——速度、容量和每位价格——导致了计算机组成中存储器的多级层次结构,其中主要是缓存和主存、主存和磁盘的结构。那么在主存之上,cache和寄存器之间的关系是?

    举个例子,当你在思考一个问题的时候,寄存器存放的是你当前正在思考的内容,cache存放的是与该问题相关的记忆,主存则存放无论与该问题是否有关的所有记忆,所以,寄存器存放的是当前CPU执行的数据,而cache则缓存与该数据相关的部分数据,因此只要保证了cache的一致性,那么寄存器拿到的数据也必然具备一致性。

二、CPU cache结构

1. 单核CPU cache结构

    在单核CPU结构中,为了缓解CPU指令流水中cycle冲突,L1分成了指令(L1P)和数据(L1D)两部分,而L2则是指令和数据共存。

2. 多核CPU cache结构

    多核CPU的结构与单核相似,但是多了所有CPU共享的L3三级缓存。在多核CPU的结构中,L1和L2是CPU私有的,L3则是所有CPU核心共享的。

三、MESI(缓存一致性)

    缓存一致性:在多核CPU中,内存中的数据会在多个核心中存在数据副本,某一个核心发生修改操作,就产生了数据不一致的问题。而一致性协议正是用于保证多个CPU cache之间缓存共享数据的一致。

    至于MESI,则是缓存一致性协议中的一个,到底怎么实现,还是得看具体的处理器指令集。

1. cache的写方式

    cache的写操作方式可以追溯到大学教程《计算机组成原理》一书。

    A. write through(写通):每次CPU修改了cache中的内容,立即更新到内存,也就意味着每次CPU写共享数据,都会导致总线事务,因此这种方式常常会引起总线事务的竞争,高一致性,但是效率非常低;

    B. write back(写回):每次CPU修改了cache中的数据,不会立即更新到内存,而是等到cache line在某一个必须或合适的时机才会更新到内存中;

    无论是写通还是写回,在多核环境下都需要处理缓存cache一致性问题。为了保证缓存一致性,处理器又提供了写失效(write invalidate)和写更新(write update)两个操作来保证cache一致性。

    A. 写失效:当一个CPU修改了数据,如果其他CPU有该数据,则通知其为无效;

    B. 写更新:当一个CPU修改了数据,如果其他CPU有该数据,则通知其跟新数据;

    写更新会导致大量的更新操作,因此在MESI协议中,采取的是写失效(即MESI中的I:ivalid,如果采用的是写更新,那么就不是MESI协议了,而是MESU协议)。

    所以,现在常见的多核CPU写方式:写命中时,核内write back,核间write invalid + cache coherency protocol;写miss,先write allocated,再走前述的写命中流程。(write allocated和no-write allocated见评论区)

2. cache line

    cache line是cache与内存数据交换的最小单位,根据操作系统一般是32byte或64byte。在MESI协议中,状态可以是M、E、S、I,地址则是cache line中映射的内存地址,数据则是从内存中读取的数据。

    工作方式:当CPU从cache中读取数据的时候,会比较地址是否相同,如果相同则检查cache line的状态,再决定该数据是否有效,无效则从主存中获取数据,或者根据一致性协议发生一次cache-to--chache的数据推送(参见MESI协议,文章最后的链接);

    工作效率:当CPU能够从cache中拿到有效数据的时候,消耗几个CPU cycle,如果发生cache miss,则会消耗几十上百个CPU cycle;

    cache的工作原理以及在主板上的结构如下两图所示:

3. 状态介绍

    MESI协议将cache line的状态分成modify、exclusive、shared、invalid,分别是修改、独占、共享和失效。

    modify:当前CPU cache拥有最新数据(最新的cache line),其他CPU拥有失效数据(cache line的状态是invalid),虽然当前CPU中的数据和主存是不一致的,但是以当前CPU的数据为准;

    exclusive:只有当前CPU中有数据,其他CPU中没有改数据,当前CPU的数据和主存中的数据是一致的;

    shared:当前CPU和其他CPU中都有共同数据,并且和主存中的数据一致;

    invalid:当前CPU中的数据失效,数据应该从主存中获取,其他CPU中可能有数据也可能无数据,当前CPU中的数据和主存被认为是不一致的;

    对于invalid而言,在MESI协议中采取的是写失效(write invalidate)。

4. cache操作

    MESI协议中,每个cache的控制器不仅知道自己的操作(local read和local write),每个核心的缓存控制器通过监听也知道其他CPU中cache的操作(remote read和remote write),今儿再确定自己cache中共享数据的状态是否需要调整。

    local read(LR):读本地cache中的数据;

    local write(LW):将数据写到本地cache;

    remote read(RR):其他核心发生read;

    remote write(RW):其他核心发生write;

    下面摘自维基百科MESI协议Operation一节:

Processor Requests to Cache includes the following operations:

  1. PrRd: The processor requests to read a Cache block.
  2. PrWr: The processor requests to write a Cache block

Bus side requests are the following:

  1. BusRd: Snooped request that indicates there is a read request to a Cache block made by another processor
  2. BusRdX: Snooped request that indicates there is a write request to a Cache block made by another processor which doesn't already have the block.

因为MESI协议为了减少主存事务(主存总线竞争),会存在cache to cache的推送数据而不去内存读数据(This protocol reduces the number of Main memory transactions with respect to the MSI protocol. This marks a significant improvement in the performance.[2]),所以,还存在以下两种操作:

  1. Flush: Snooped request that indicates that an entire cache block is written back to the main memory by another processor.
  2. FlushOpt: Snooped request that indicates that an entire cache block is posted on the bus in order to supply it to another processor(Cache to Cache transfers).

local read/write的操作(当前处理器自己的cache读写):

Table 1.1 State Transitions and response to various Processor Operations
Initial State Operation Response
Invalid(I) PrRd
  • Issue BusRd to the bus
  • other Caches see BusRd and check if they have a non-invalid copy, inform sending cache
  • State transition to (S)Shared, if other Caches have non-invalid copy.
  • State transition to (E)Exclusive, if none (must ensure all others have reported).
  • If other Caches have copy, one of them sends value, else fetch from Main Memory
PrWr
  • Issue BusRdX signal on the bus
  • State transition to (M)Modified in the requestor Cache.
  • If other Caches have copy, they send value, otherwise fetch from Main Memory
  • If other Caches have copy, they see BusRdX signal and Invalidate their copies.
  • Write into Cache block modifies the value.
Exclusive(E) PrRd
  • No bus transactions generated
  • State remains the same.
  • Read to the block is a Cache Hit
PrWr
  • No bus transaction generated
  • State transition from Exclusive to (M)Modified
  • Write to the block is a Cache Hit
Shared(S) PrRd
  • No bus transactions generated
  • State remains the same.
  • Read to the block is a Cache Hit.
PrWr
  • Issues BusUpgr signal on the bus.
  • State transition to (M)Modified.
  • other Caches see BusUpgr and mark their copies of the block as (I)Invalid.
Modified(M) PrRd
  • No bus transactions generated
  • State remains the same.
  • Read to the block is a Cache hit
PrWr
  • No bus transactions generated
  • State remains the same.
  • Write to the block is a Cache hit.

remote read/write的操作(其他处理器cache的读写操作对当前处理器cache line状态的影响):

Table 1.2 State Transitions and response to various Bus Operations
Initial State Operation Response
Shared(S) BusRd
  • No State change (other cache performed read on this block, so still shared).
  • May put FlushOpt on bus together with contents of block (design choice, which cache with Shared state does this).
BusRdX/BusUpgr
  • Transition to (I)Invalid.
  • Put FlushOpt on Bus with data. Received by sender of BusRdx and Memory Controller, which writes to Main memory.
Invalid(I) BusRd
  • No State change. Signal Ignored.
BusRdX
  • No State change. Signal Ignored
Modified(M) BusRd
  • Transition to (S)Shared.
  • Put FlushOpt on Bus with data. Received by sender of BusRd and Memory Controller, which writes to Main memory.
BusRdX
  • Transition to Invalid (cache that sent BusRdX becomes Modified)
  • May put FlushOpt on bus together with contents of block (design choice, which cache with Shared state does this)
Exclusive(E) BusRdX
  • Transition to Invalid.
  • Put FlushOpt on Bus, together with the data from now-invalidated block.
BusRd
  • Transition to Shared (Since it implies a read taking place in other cache).
  • Put FlushOpt on bus together with contents of block.

5. 状态转换和cache操作

    如上文内容所述,MESI协议中cache line数据状态有4种,引起数据状态转换的CPU cache操作也有4种,因此要理解MESI协议,就要将这16种状态转换的情况讨论清楚。

    初始场景:在最初的时候,所有CPU中都没有数据,某一个CPU发生读操作,此时必然发生cache miss,数据从主存中读取到当前CPU的cache,状态为E(独占,只有当前CPU有数据,且和主存一致),此时如果有其他CPU也读取数据,则状态修改为S(共享,多个CPU之间拥有相同数据,并且和主存保持一致),如果其中某一个CPU发生数据修改,那么该CPU中数据状态修改为M(拥有最新数据,和主存不一致,但是以当前CPU中的为准),其他拥有该数据的核心通过缓存控制器监听到remote write行文,然后将自己拥有的数据的cache line状态修改为I(失效,和主存中的数据被认为不一致,数据不可用应该重新获取)。

5.1 modify

    场景:当前CPU中数据的状态是modify,表示当前CPU中拥有最新数据,虽然主存中的数据和当前CPU中的数据不一致,但是以当前CPU中的数据为准;

    LR:此时如果发生local read,即当前CPU读数据,直接从cache中获取数据,拥有最新数据,因此状态不变;

    LW:直接修改本地cache数据,修改后也是当前CPU拥有最新数据,因此状态不变;

    RR:因为本地内存中有最新数据,当本地cache控制器监听到总线上有RR发生的时,必然是其他CPU发生了读主存的操作,此时为了保证一致性,当前CPU应该将数据写回主存,而随后的RR将会使得其他CPU和当前CPU拥有共同的数据,因此状态修改为S;

    RW:同RR,当cache控制器监听到总线发生RW,当前CPU会将数据写回主存,因为随后的RW将会导致主存的数据修改,因此状态修改成I;

5.2 exclusive

    场景:当前CPU中的数据状态是exclusive,表示当前CPU独占数据(其他CPU没有数据),并且和主存的数据一致;

    LR:从本地cache中直接获取数据,状态不变;

    LW:修改本地cache中的数据,状态修改成M(因为其他CPU中并没有该数据,因此不存在共享问题,不需要通知其他CPU修改cache line的状态为I);

    RR:本地cache中有最新数据,当cache控制器监听到总线上发生RR的时候,必然是其他CPU发生了读取主存的操作,而RR操作不会导致数据修改,因此两个CPU中的数据和主存中的数据一致,此时cache line状态修改为S;

    RW:同RR,当cache控制器监听到总线发生RW,发生其他CPU将最新数据写回到主存,此时为了保证缓存一致性,当前CPU的数据状态修改为I;

5.3 shared

    场景:当前CPU中的数据状态是shared,表示当前CPU和其他CPU共享数据,且数据在多个CPU之间一致、多个CPU之间的数据和主存一致;

    LR:直接从cache中读取数据,状态不变;

    LW:发生本地写,并不会将数据立即写回主存,而是在稍后的一个时间再写回主存,因此为了保证缓存一致性,当前CPU的cache line状态修改为M,并通知其他拥有该数据的CPU该数据失效,其他CPU将cache line状态修改为I;

    RR:状态不变,因为多个CPU中的数据和主存一致;

    RW:当监听到总线发生了RW,意味着其他CPU发生了写主存操作,此时本地cache中的数据既不是最新数据,和主存也不再一致,因此当前CPU的cache line状态修改为I;

5.4 invalid

    场景:当前CPU中的数据状态是invalid,表示当前CPU中是脏数据,不可用,其他CPU可能有数据、也可能没有数据;

    LR:因为当前CPU的cache line数据不可用,因此会发生读内存,此时的情形如下。

        A. 如果其他CPU中无数据则状态修改为E;

        B. 如果其他CPU中有数据且状态为S或E则状态修改为S;

        C. 如果其他CPU中有数据且状态为M,那么其他CPU首先发生RW将M状态的数据写回主存并修改状态为S,随后当前CPU读取主存数据,也将状态修改为S;

    LW:因为当前CPU的cache line数据无效,因此发生LW会直接操作本地cache,此时的情形如下。

        A. 如果其他CPU中无数据,则将本地cache line的状态修改为M;

        B. 如果其他CPU中有数据且状态为S或E,则修改本地cache,通知其他CPU将数据修改为I,当前CPU中的cache line状态修改为M;

        C. 如果其他CPU中有数据且状态为M,则其他CPU首先将数据写回主存,并将状态修改为I,当前CPU中的cache line转台修改为M;

    RR:监听到总线发生RR操作,表示有其他CPU读取内存,和本地cache无关,状态不变;

    RW:监听到总线发生RW操作,表示有其他CPU写主存,和本地cache无关,状态不变;

5.5 总结

    MESI协议为了保证多个CPU cache中共享数据的一致性,定义了cache line的四种状态,而CPU对cache的4种操作可能会产生不一致状态,因此cache控制器监听到本地操作和远程操作的时候,需要对地址一致的cache line状态做出一定的修改,从而保证数据在多个cache之间流转的一致性。

四、store buffer和invalid queue

    这里算是一些小的补充,为了加速MESI协议中的一些操作,硬件上引入store buffer和invalid queue的原因。

以下来自于http://en.wikipedia.org/wiki/MESI_protocol


内存壁垒


MESI原型(naive MESI)直接的实现方式表现出两种特殊的低性能行为:首先,当写入无效的缓存行时,从另一个CPU提取该行时会存在较长的延迟;其次,将缓存行移至无效状态非常耗时。

因此,CPU实现了存储缓冲区并使队列无效。

写入无效的缓存行时使用存储缓冲区。由于无论如何都会进行写操作,因此CPU会发出一条读无效的消息(因此,有问题的高速缓存行以及存储该内存地址的所有其他CPU的高速缓存行都会无效),然后将写操作推入存储缓冲区当高速缓存行最终到达时执行。(CPU在尝试读取高速缓存行时会扫描自己的存储缓冲区,以防万一它有准备写入高速缓存的内容)。

因此,一个CPU可以写一些东西,但是还没有写在缓存中,因此其他CPU *看不到*-他们无法扫描其他CPU的存储缓冲区。

关于无效,CPU实施无效队列,从而立即确认传入的无效请求,但实际上不对其采取任何行动-它们只是输入无效队列,尽快进行处理(但不一定立即进行)。这样,CPU可以在其高速缓存中包含无效的行,但是尚不知道该行无效的行-无效队列包含尚未作用的无效。(失效队列在高速缓存的另一侧; CPU无法对其进行扫描,因为它可以对存储缓冲区进行扫描)。

结果,需要存储屏障。存储屏障将刷新存储缓冲区(确保所有写入均已进入CPU缓存)。读取屏障将刷新失效队列(确保其他CPU的所有写操作对刷新CPU可见)。

因此,MESI在实践中不太有效-如果您是单线程的,这不是问题,但如果不是,则绝对是问题。


以下来自于
http://remonstrate.wordpress.com/tag/invalidate-queue/

  • CPU 的速度与访问内存的速度差距巨大,为此 CPU 设计者使用了 cache,为每个 CPU/core 提供一个相对快速访问的存储空间,有了 cache 意味着存在 replication,这本质上跟分布式系统面临的问题类似,只是单机上我们可以使用一些硬件上的东西来提供某种 consistency model
  • cache 类似一个 hash table,但是其 hash 比较简单,通常是地址计算出来的 bit set
  • 为了使得 CPU 访问一致的数据,人们通过 cache coherence protocol 来同步 cache 里面的数据,常见的协议是 MESI 及其变种
  • MESI 对应四个状态:modified(被修改过,要求对应内存不存在其他 cache 中)、exclusive(尚未被修改,但是仅有这个 CPU 拥有 copy,对应的也是 up-to-date 的数据)、shared(可能被多个 CPU 拥有,此时可以读但不可以写) 和 invalid(表示数据非法,也就是可以看成不可用);通常 CPU 需要用个 2bit 表示这四个状态;状态之间的转移通过某种 meesage 来实现
  • 一般对 MESI 的简单实现都是没有实际价值,这是因为发生写操作往往会带来很长时间的等候:首先需要写的 CPU 需要让别的 CPU 将状态转换到 invalid,收到 response 以后才能进行实际的写,为此硬件专家使用了 store buffer(Sutter 同志也说过,modern CPU 如果没有 store buffer 就不值得买,可见这个 feature 对整体性能的影响是不可忽略的)
  • store buffer 的作用是让 CPU 需要写的时候仅仅将其操作交给 store buffer,然后继续执行下去,store buffer 在某个时刻就会完成一系列的同步行为;很明显这个简单的东西会违背 self consistency,因为如果某个 CPU 试图写其他 CPU 占有的内存,消息交给 store buffer 后,CPU 继续执行后面的指令,而如果后面的指令依赖于这个被写入的内存(尚未被更新,这个时候读取的值是错误的)就会产生问题,所以实际实现 store buffer 可能会增加 snoop 特性,即 CPU 读取数据时会从 store buffer 和 cache 两处读
  • 即便增加了 snoop,store buffer 仍然会违背 global memory ordering,导致的解决方案是 memory barrier:我们知道程序员书写两个写操作的时候,隐含的假定是如果能观察到后一个写的结果,那么前一个写的结果势必也会发生,这是一个非常符合人直觉的行为,但是由于 store buffer 的存在,这个结论可能并不正确:这是因为如果观察线程位于另一个 core,首先读取后一个写(该地址并不在 cache 内)需要向写入线程所在 core 要对应地址的值,由于该 core 从 store buffer 返回了新值的时候这个 buffer 里面的写操作可能尚未发生,所以观察线程在获取了后一个写的最新结果时,前一个写的结果依然无法观察到,这违背了 sequential consistency 的假定,往往程序员更倾向于这个 consistency model 下的 reasoning
  • 硬件 level 上很难揣度软件上这种前后依赖关系,因此往往无法通过某种手段自动的避免这种问题,因而只有通过软件的手段表示(对应也需要硬件提供某种指令来支持这种语义),这个就是 memory barrier,从硬件上来看这个 barrier 就是 CPU flush 其 store buffer 的指令,那么一种做法就是提供给程序员对应的指令(封装到函数里面)要求在合适的时候插入表达这种关系,另一种做法就是通过某种标识让编译器翻译的时候自动的插入这个指令
  • 往往 store buffer 都很小,针对连续写操作力不从心;类似的情况也发生在碰到 memory barrier 之后;开始写之前首先需要 invalidate 其他 cache 里面的数据,为了加速这个过程硬件设计者又加入了 invalidate queue,这个 queue 将 incoming 的 invalidate 消息存放,立即返回对应的 response 这样以便发起者能尽快做后面的事情,而这个 CPU 可以通过 invalidate queue 后续处理这些内存
  • invalidate queue 的存在会使得我们有更多的地方需要 memory barrier(理由与 store buffer 类似)
  • 实际 memory barrier 又有一些细分,如 read/write 的,软件上会通过 smb_mb/rmb/wmb 等表示,对应的硬件指令不同平台下各不相同
  • 实际实现的时候由于某些指令集之间的关系使得 memory barrier 的实现不可能做到最优,很多常见的平台都使用了简单粗暴的 bus 锁(x86、amd64、armv7),这也就是 Sutter talk 里面认为硬件平台往往提供了一些“过度”的指令的原因,最终软件需要的 sequential consistency 尽管得以实现,但是产生了一些不必要的代价
  • memory barrier 不是一个必需的东西,但是似乎如果有 real-time 的 requirement 似乎就不能避开,这一点有待证实

实际的例子:http://sstompkins.wordpress.com/2011/04/12/why-memory-barrier%EF%BC%9F/

五、资源奉送

    blog:点击打开链接

    book:《大话处理器》(支持原著,请购买正版)

    MESI(维基百科)

缓存相关的写策略:write back/write through,write allocate no-write allocate

计算机体系结构:推荐国防科大张晨曦《计算机体系结构》

what is store buffer(StackOverFlow问答)

附注:

    本文如有错漏,烦请不吝指正,谢谢!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【并发编程】CPU cache结构和缓存一致性(MESI协议) 的相关文章

  • ReentrantLock 源码分析

    ReentrantLock简单使用demo如下 Lock lock new ReentrantLock lock lock try 业务逻辑 finally lock unlock 注 获取的锁代码要放到try块之外 防止获得锁代码异常 抛
  • 云计算与Kubernetes(k8s)

    参考链接 https blog csdn net zkkzpp258 article details 86541362 https blog csdn net Bubbler 726 article details 85596418 htt
  • 几种自旋锁SpinLock,TicketLock,CLHLock,以及可重入实现要点,非阻塞锁实现要点

    最核心的东西 synchronization state 同步状态 指示当前线程是否可以proceed还是需要wait的状态 1 普通SpinLock 支持可重入的版本 class SpinLock use thread itself as
  • jdbc对oracle RAC方式的支持

    1 JDBC连接的配置方式 既然数据库本身已经支持了RAC 那么只要在JDBC连接字符串中将RAC的配置加上去即可 根据资料和目前的业务需求 我们的针对HOST 10 87 25 37这台主机的数据库实例的JDBC连接字符串应该这样配置 U
  • Jdk1.8新特性 - 方法引用

    一 说明 方法引用使用一对冒号 标识 通过方法的名字来指向一个方法 是函数式接口的另一种书写方式 通过方法引用 可以将方法的引用赋值给一个Function变量 Lambda表达式一般用于自己提供方法体 而方法引用一般直接引用现成的方法 二
  • Java中synchronized同步锁用法及作用范围

    Java 中的 synchronized 关键字可以在多线程环境下用来作为线程安全的同步锁 本文主要对 synchronized 的作用 以及其有效范围进行讨论 Java中的对象锁和类锁 java的对象锁和类锁在锁的概念上基本上和内置锁是一
  • 分布式并发查询结果不重复可行性方案

    场景 接口逻辑 资源池获取数据 并在拿到数据后进行更新 问题 高并发下 获取的结果集相同的概率大 资源争抢更新 导致并发的相同的数据后更新的异常 尝试方案 序号 方案 问题 1 获取结果集100个 随机挑选一个 不可行 并发和随机到同一个的
  • 线程结束的三种方式

    停止一个线程通常意味着在线程处理任务完成之前停掉正在做的操作 也就是放弃当前的操作 在 Java 中有以下 3 种方法可以终止正在运行的线程 使用退出标志 使线程正常退出 也就是当 run 方法完成后线程中止 使用 stop 方法强行终止线
  • java高并发的处理--锁机制

    对于我们开发的网站 如果网站的访问量非常大的话 那么我们就需要考虑相关的并发访问问题了 而并发问题是绝大部分的程序员头疼的问题 但话又说回来了 既然逃避不掉 那我们就坦然面对吧 今天就让我们一起来研究一下常见的并发和同步吧 为了更好的理解并
  • 处理高并发、高访问之Apache优化

    前言 项目100人同时访问 导致访问速度变慢 作为一个没有遇到过这种情况下的辕 在各种查阅资料后 先用删除日志更改日志输出的方法处理后 处理方法 修改Apache日志输出相关配置方法 暂时好缓 后来又出现变慢 在查阅各种博客后 发现一个处理
  • SSM实战项目——Java高并发秒杀API

    SSM实战项目 Java高并发秒杀API 项目截图 秒杀列表 秒杀详情页 错误提示 开始秒杀 秒杀成功 重复秒杀 秒杀倒计时 秒杀结束 项目介绍 何为秒杀 所谓 秒杀 就是网络卖家发布一些超低价格的商品 所有买家在同一时间网上抢购的一种销售
  • 如何理解Zookeeper的顺序一致性

    2017饿了么做异地多活 我的团队承担Zookeeper的异地多活改造 在此期间我听到2种不同的关于一致性的说法 一种说法是Zookeeper是最终一致性 因为由于多副本 以及保证大多数成功的Zab协议 当一个客户端进程写入一个新值 另外一
  • Java中 Happen-before 规则总结

    详细见 http docs oracle com javase 7 docs api java util concurrent package summary html 比较重要的几条 1 Actions prior to releasin
  • java 关于锁常见面试题

    1 synchronized作用于静态方法和非静态方法的区别 非静态方法 给对象加锁 可以理解为给这个对象的内存上锁 注意 只是这块内存 其他同类对象都会有各自的内存锁 这时候在其他一个以上线程中执行该对象的这个同步方法 注意 是该对象 就
  • Java线程池的使用(简单实现)

    一 线程池的概念 创建Java线程需要给线程分配堆栈内存以及初始化内存 还需要进行系统调用 频繁地创建和销毁线程会大大降低系统的运行效率 采用线程池来管理线程有以下好处 提升性能 线程池能独立负责线程的创建 维护和分配 线程管理 每个Jav
  • 【并发编程】CPU cache结构和缓存一致性(MESI协议)

    一 cache cpu cache已经发展到了三级缓存结构 基本上现在买的个人电脑都是L3结构 1 cache的意义 为什么需要CPU cache 因为CPU的频率太快了 快到主存跟不上 这样在处理器时钟周期内 CPU常常需要等待主存 浪费
  • MySQL最常用的二种存储引擎MyISAM和InnoDB的介绍

    1 MyISAM 默认表类型 它是基于传统的ISAM类型 ISAM是Indexed Sequential Access Method 有索引的顺序访问方法 的缩写 它是存储记录和文件的标准方法 不是事务安全的 而且不支持外键 如果执行大量的
  • Jmeter动态吞吐量实现

    在容量测试时 控量 是非常重要的 JMeter 是根据线程数大小来控制压力强弱的 但我们制定的压测目标中的指标往往是吞吐量 QPS TPS 这就给测试人员带来了不便之处 必须一边调整线程数 一边观察 QPS TPS 达到什么量级了 为了解决
  • django高并发部署

    django高并发部署
  • Java并发基础--CPU性能优化与内存屏障

    为了提高程序运行的性能 现代CPU在很多方面对程序进行了优化 1 CPU高速缓存 尽可能地避免处理器访问主内存的时间开销 处理器大多会利用高速缓存以提高性能 CPU缓存分为多几缓存 如图 L1 Cache 一级缓存 是CPU第一层高速缓存

随机推荐

  • linux 网络

    网络基础 协议的概念 什么是协议 从应用的角度出发 协议可理解为 规则 是数据传输和数据的解释的规则 假设 A B双方欲传输文件 规定 第一次 传输文件名 接收方接收到文件名 应答OK给传输方 第二次 发送文件的尺寸 接收方接收到该数据再次
  • tf好朋友之matplotlib的使用——subplot分格显示

    tf好朋友之matplotlib的使用 subplot分格显示 分格显示的方法 利用plt subplot2grid进行分格显示 利用gridspec GridSpec进行分格显示 应用示例 在学习matlab的时候 图像是可以分格显示的
  • 1032. 挖掘机技术哪家强(20)

    1032 挖掘机技术哪家强 20 时间限制 200 ms 内存限制 65536 kB 代码长度限制 8000 B 判题程序 Standard 作者 CHEN Yue 为了用事实说明挖掘机技术到底哪家强 PAT组织了一场挖掘机技能大赛 现请你
  • maven仓库中_remote.repositories的作用

    首先直接给结论 remote repositories的作用是当maven本地仓库缓存了jar pom的情况下修改了maven的配置文件 settings xml 后依然会去远程仓库获取 以org slf4j slf4j api 1 5 6
  • 科创板、香港主板、纳斯达克三地部门上市条件和要求

    转自 https zhuanlan zhihu com p 69144513 科创板 香港主板 纳斯达克三地部门上市条件和要求 发布于 2019 06 14
  • 一、创建型模式:工厂方法模式(Factory Method)

    请MM去麦当劳吃汉堡 不同的MM有不同的口味 要每个都记住是一件烦人的事情 我一般采用Factory Method模式 带着MM到服务员那儿 说 要一个汉堡 具体要什么样的汉堡呢 让MM直接跟服务员说就行了 定义 核心工厂类不再负责所有产品
  • MySQL-多表关联

    多表关联 多张数据表之间是可以有一定的关联关系 这种关联关系可以通过外键约束实现 多表的分类 一对一 一对多 多对多 一对一 一张表对应另一张表 适用场景 人和身份证 一个人只能有一个身份证 一个身份证只能对应一个人 建表原则 在任意一个表
  • python爬虫实战练手——————淘宝网站的爬取

    python爬虫是很好的数据分析手段 可以进行爬虫程序来进行爬取网站 下面是淘宝的爬取 淘宝搜索书包 然后得到以下的界面 注意到下面的分页 可以通过进行分页的改变来进行多页数据的爬取 爬取多页 这里用到了和重要的re库 也就是正则表达式库
  • Windows10下Linux子系统Ubuntu使用教程(8)——升级WSL2,及解决遇到的问题

    WSL 2 是 WSL 中体系结构的新版本 它更改 Linux 发行版与 Windows 交互的方式 WSL 2 的主要目标是提高文件系统性能并增加系统调用的完全兼容性 每个 Linux 发行版都可以作为 WSL 1 或 WSL 2 发行版
  • vue 获取服务端base64位图片之后的处理

    目录 Base64是什么 Base64可以在Url中传输吗 Base64是加密算法么 Base64的应用场景有哪些 Base64的优点 Base64的缺点 关于vue中img无法展示base64位图片的原因分析 Base64是什么 Base
  • CSS-选择器的基本用法

    目录 一 CSS的分类 1 行内样式 2 内部样式 3 外部样式 二 选择器是什么 三 选择器具体种类 1 类选择器 2 标签选择器 3 ID选择器 4 通配符选择器 一 CSS的分类 1 行内样式 通过 style 属性 来指定某个标签的
  • Java实现Token的生成与验证

    二 基于JWT的token认证实现 JWT JSON Web Token 其实token就是一段字符串 由三部分组成 Header Payload Signature 1 引入依赖
  • 爬虫之简单js逆向

    本次js逆向没有存在代码混淆 所以还是比较简单的 重要的就是js逆向的思路 目标网站https notice qb com detail noticeId 215让我们开始吧 进入网站后按F12 查看DOC中的 可以看出该网页一部分内容是异
  • vue3解读—reactivity响应式实现

    前言 Vue3 中引入了proxy进行数据劫持 而effect是响应式系统的核心 而响应式系统又是 vue3 中的核心 所以vue3的解读要从 effect 开始讲起 1 reactivity和effect的使用 目前vue3的各个模块都可
  • 蓝桥杯:基础练习 特殊的数字(java实现)

    问题描述 153是一个非常特殊的数 它等于它的每位数字的立方和 即153 111 555 333 编程求所有满足这种条件的三位十进制数 输出格式 按从小到大的顺序输出满足条件的三位十进制数 每个数占一行 public class Main
  • 2014阿里巴巴9月14北京校园招聘笔试及参考答案

    form http blog csdn net lingfengtengfei article details 12344511 from http blog csdn net lingfengtengfei article details
  • java实现根据pdf文件模板生成pdf文件

    一 如何制作pdf模板 1 首先创建template doc 2 根据doc文件制作pdf模板 3 将doc文件输出为pdf 文件 gt 输出为pdf 4 输出的pdf文件 5 使用Adobe Acrobat DC打开template pd
  • HTML+CSS炫酷效果(小伙伴赶紧收藏起来吧)

    制作不易 点赞加关注哦 目录 1 实现奥运徽效果 2 实现3D效果 3 翻开葵花宝典 4 实现漂浮文字 动图 5 手机充电特效 动态 6 滚动时针 7 立体相册 1 实现奥运徽效果 由于图片违规 就不给老铁发了哈 亲自试试呗 HTML如下
  • 对 Electron 架构的理解

    Electron 的架构可以分为三层 Chromium Node js 和应用程序层 Electron 是一种基于 Chromium 和 Node js 的开源框架 可以用于快速构建跨平台的桌面应用程序 Chromium 层 Chromiu
  • 【并发编程】CPU cache结构和缓存一致性(MESI协议)

    一 cache cpu cache已经发展到了三级缓存结构 基本上现在买的个人电脑都是L3结构 1 cache的意义 为什么需要CPU cache 因为CPU的频率太快了 快到主存跟不上 这样在处理器时钟周期内 CPU常常需要等待主存 浪费