1. cpu中的cache结构及cache一致性
一. 引子
在多线程环境中,经常会有一些计数操作,用来统计线上服务的一些qps、平均延时、error等。为了完成这些统计,可以实现一个多线程环境下的计数器类库,方便记录和查看用户程序中的各类数值。在实现这个计数器类库时,可以利用thread local存储来避免cache bouncing,从而提高效率。注意,这种实现方式的本质是把写时的竞争转移到了读:读得合并所有写过的线程中的数据,而不可避免地变慢了。当你读写都很频繁并得基于数值做一些逻辑判断时,你不应该用前述的实现方式。那么,cache bouncing是什么?下面详细说明一下。
二. 什么是cache bouncing?
为了以较低的成本大幅提高性能,现代CPU都有cache。cpu cache已经发展到了三级缓存结构,基本上现在买的个人电脑都是L3结构。其中L1和L2cache为每个核独有,L3则所有核共享。为了保证所有的核看到正确的内存数据,一个核在写入自己的L1 cache后,CPU会执行Cache一致性算法把对应的cacheline(一般是64字节)同步到其他核。这个过程并不很快,是微秒级的,相比之下写入L1 cache只需要若干纳秒。当很多线程在频繁修改某个字段时,这个字段所在的cacheline被不停地同步到不同的核上,就像在核间弹来弹去,这个现象就叫做cache bouncing。由于实现cache一致性往往有硬件锁,cache bouncing是一种隐式的的全局竞争。
cache bouncing使访问频繁修改的变量的开销陡增,甚至还会使访问同一个cacheline中不常修改的变量也变慢,这个现象是false sharing。按cacheline对齐能避免false sharing,但在某些情况下,我们甚至还能避免修改“必须”修改的变量。当很多线程都在累加一个计数器时,我们让每个线程累加私有的变量而不参与全局竞争,在读取时我们累加所有线程的私有变量。虽然读比之前慢多了,但由于这类计数器的读多为低频展现,慢点无所谓。而写就快多了,从微秒到纳秒,几百倍的差距。
三. cache
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(缓存一致性)
cache的写操作方式可以追溯到大学教程《计算机组成原理》一书。
A. write through(写通):每次CPU修改了cache中的内容,立即更新到内存,也就意味着每次CPU写共享数据,都会导致总线事务,因此这种方式常常会引起总线事务的竞争,高一致性,但是效率非常低;
B. write back(写回):每次CPU修改了cache中的数据,不会立即更新到内存,而是等到cache line在某一个必须或合适的时机才会更新到内存中;
无论是写通还是写回,在多线程环境下都需要处理缓存cache一致性问题。为了保证缓存一致性,处理器又提供了写失效(write invalidate)和写更新(write update)两个操作来保证cache一致性。
写失效:当一个CPU修改了数据,如果其他CPU有该数据,则通知其为无效;
写更新:当一个CPU修改了数据,如果其他CPU有该数据,则通知其跟新数据;
写更新会导致大量的更新操作,因此在MESI协议中,采取的是写失效(即MESI中的I:ivalid,如果采用的是写更新,那么就不是MESI协议了,而是MESU协议)。
2. cache line
cache line是cache与内存数据交换的最小单位,根据操作系统一般是32byte或64byte。在MESI协议中,状态可以是M、E、S、I,地址则是cache line中映射的内存地址,数据则是从内存中读取的数据。
工作方式:当CPU从cache中读取数据的时候,会比较地址是否相同,如果相同则检查cache line的状态,再决定该数据是否有效,无效则从主存中获取数据,发起一次RR(remote read);
工作效率:当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)。对于自己本地缓存有的数据,CPU仅需要发起local操作,否则发起remote操作,从主存中读取数据,cache控制器通过总线监听,仅能够知道其他CPU发起的remote操作,但是如果local操作会导致数据不一致性,cache控制器会通知其他CPU的cache控制器修改状态。
local read(LR):读本地cache中的数据;
local write(LW):将数据写到本地cache;
remote read(RR):读取内存中的数据;
remote write(RW):将数据写通到主存;
5. 状态转换和cache操作
如上文内容所述,MESI协议中cache line数据状态有4种,引起数据状态转换的CPU cache操作也有4种,因此要理解MESI协议,就要将这16种状态转换的情况讨论清楚。
MESI协议为了保证多个CPU cache中共享数据的一致性,定义了cache line的四种状态,而CPU对cache的4种操作可能会产生不一致状态,因此cache控制器监听到本地操作和远程操作的时候,需要对地址一致的cache line状态做出一定的修改,从而保证数据在多个cache之间流转的一致性。
2. 客户端-服务器模式架构
客户端-服务器模式
定义:客户端-服务器模式(Client–server model)简称C/S结构,是一种网络架构,它把客户端 (Client) 与服务器 (Server) 区分开来。每一个客户端软件的实例都可以向一个服务器或应用程序服务器发出请求。
C/S结构:Client/Server结构(C/S结构)是大家熟知的客户机和服务器结构。它是软件系统体系结构,通过它可以充分利用两端硬件环境的优势,将任务合理分配到Client端和Server端来实现,降低了系统的通讯开销。
客户端进程:
这是一个过程,这通常使得信息请求。得到回应后,这一过程可能会终止或可能会做一些其他的处理。
例如: 互联网浏览器作为一个客户端应用程序,Web服务器发送一个请求到得到一个HTML网页。
服务器进程:
它接受一个来自客户端的请求的过程。获得来自客户端的请求后会处理所需的收集所需的信息,将其发送到请求客户端。一旦这样做完成后,就又变成准备为另一个客户端。服务器进程始终等待准备用于处理传入请求。
实例: Web服务器一直等待来自互联网浏览器的请求,并尽快得到任何请求从浏览器,它拿起一个请求的HTML页面,并把它发送回该浏览器。
注意,客户端需要知道的存在服务器的地址,但是服务器并不需要在建立的连接之前知道客户端的地址。一旦建立连接后,双方都可以发送和接收信息。
2层和3层架构:
有两种类型的客户端服务器架构:
-
两层构架: 在这种架构中,客户端直接与服务器进行交互。这种类型的架构可能有一些安全漏洞和性能问题。 IE浏览器和Web服务器的两层架构。这里的安全问题都解决了使用安全套接字层(SSL)。
-
三层架构:在这个架构中,多了一个软件位于客户端和服务器之间。这中间的软件被称为中间件。中间件被用来执行所有的安全检查和重负载情况下的负载平衡。中间件需要从客户端的所有请求,并做必要的验证后,通过向服务器发出请求。然后,服务器没有所需的处理和发送响应回中间件,中间件终于通过这个响应返回给客户端。如果想实现一个3层架构,那么可以使用如Web Logic或WebSphere软件在Web服务器和Web浏览器之间的任何中间件。
优点:
(1)可实现资源共享。C/L结构中的资源是分布的,客户机与服务器具有一对多的关系和运行环境。用户不仅可存取在服务器和本地工作站上的资源,还可以享用其他工作站上的资源,实现了资源共享。
(2)可实现管理科学化和专业化。系统中的资源分布在各服务器和工作站上,可以采用分层管理和专业化管理相结合的方式,用户有权去充分利用本部门、本领域的专业知识来参与管理,使得各级管理更加科学化和专业化。
(3)可快速进行信息处理。由于在 C/S 结构中是一种基于点对点的运行环境,当一项任务提出请求处理时,可以在所有可能的服务器间均衡地分布该项任务的负载。这样,在客户端发出的请求可由多个服务器来并行进行处理,为每一项请求提供了极快的响应速度和较高的事务吞吐量。
(4)能更好地保护原有的资源。由于C/S是一种开放式的结构,可有效地保护原有的软、硬件资源。以前,在其他环境下积累的的数据和软件均可在C/S中通过集成而保留使用,并且可以透明地访问多个异构的数据源和自由地选用不同厂家的数据应用开发工具,具有高度的灵活性;而以前的硬件亦可完全继续使用,当在系统中增加硬件资源时,不会减弱系统的能力,同时客户机和服务器均可单独地升级,故具有极好的可扩充性。
服务端的特征:
-
被动的角色(从)。
-
等待来自用户端的要求。
-
处理要求并传回结果。
客户端特征:
-
主动的角色(主)。
-
发送要求。
-
等待直到收到回应。
3. 局域网通信原理
上图中连线应该是双向箭头,这里就不在进行修改了
局域网中A和B进行通信,可以直接连接网线。但当局域网中计算机比较多的时候呢?都连接网线吗?显然这种做法不太行。由此交换机就应运而生。
局域网间多个计算机进行相互通信,依靠的就是交换机。下面说一下计算机之间进行通讯的详细流程。在这之前先了解一下网络的层次。这里以五层为例;
================================
应用层
传输层(端口号 TCP.UDP等协议)
网络层
数据链路层(ARP地址解析协议)
物理层 (网线等)
================================
比如A、C间进行通信:
A第一次找C时,会通过广播的形式来通知局域网内的其他计算机(这里为B,C)。C则会通过地址解析协议(ARP)将自己的IP与MAC进行绑定,然后发送给A。A中此时有C的
IP-MAC,下次A会将MAC地址发送给交换机,从而查找C,进而找到C中对应的进程端口号(计算机之间的交互,其实就是各个进程之间的交互)。