Cache 与 Memory
由于CPU的速度远高于Memory,Cache 是位于CPU与内存(Memory,RAM)之间的临时存储器,它的容量比内存小但交换速度快,寄存了RAM位置的地址及数据内容。当CPU要读取RAM位置数据时,首先从Cache中查找,如果找到就立即读取并送给CPU处理;如果没有找到,就从Memory中读取并送给CPU处理,同时把这个数据所在的数据块调入Cache中。对Cache写数据须标注或Memory同步。Cache又分为一级(L1)、二级(L2)和三级(L3),L1/L2 Cache集成在CPU内部,L3 Cache焊在主板上。
Cache 与 Buffer
Cache与Buffer有可能指硬件实现,如硬盘自带缓存、CPU Cache,也可能指软件实现也有,如web服务器缓存、浏览器缓存。一般而言,Cache更倾向于读相关,Buffer更偏向于写相关,但在某些场景下读与写是兼备的。下边讲的cache和buffer是内存空间中的概念,运行 free -m 时将输出如下信息:
cenze@kylin:~$ free -m total used free shared buff/cache available Mem: 3898 2003 185 229 1709 1390 Swap: 3914 0 3914
buffer (cache)指块设备的读写缓冲,linux有一个守护进程定期写buffer内容(包括已修改的 i-node、已延迟的块 I/O 和读写映射文件)到disk,或手动运行sync命令写buffer数据到disk;(page) cache指文件系统缓存;shared主要用于不同进程之间共享数据,是进程间通信的一种方法,一般的应用程序不会申请使用共享内存。
A buffer is something that has yet to be written to disk.
A cache is something that has been read from the disk and stored for later use.
引文中的Buffer和Cache还是指的系统为Disk分配的内存空间。Disk Buffer主要是为写(缓冲)用的,多次数据写可以先写入Disk Buffer,最后一次性做慢速的Disk写,能起到保护Disk的作用;Disk Cache主要是为读(缓存)用的,这里不再赘述。
堆(Heap)与栈(Stack)
在不同语境下,堆与栈的含义不同。
◊ 内存中的堆栈分区:
(1)栈是为线程执行预留的内存空间,每一个线程都有一个对应的栈,栈数据不能在栈间共享(Java栈内部多个值相等的变量是可以指向同一个地址的)。栈由编译器自动分配释放 ,临时存放函数参数、局部变量等大小和生存期确定的数据,用完立即释放。
函数调用过程中的返回地址、参数和局部变量都采用栈的方式存放。 在函数调用时,在大多数的C编译器中,第一个进栈的是发生函数调用的下一条执行语句地址(即返回地址), 然后参数由右往左入栈,再然后是函数中的局部变量。
栈以LIFO方式存取,系统为栈使用一级缓存,速度较快;
栈是机器系统提供的数据结构,计算机在硬件底层对栈提供支持,如分配专门的寄存器存放栈的地址和 执行压栈出栈指令,因此栈的访问效率高。
栈空间连续,最大容量有限制(当栈空间耗尽时产生栈溢出错误:Stack Overflow),
cenze@kylin:~$ ulimit -a core file size (blocks, -c) 0 data seg size (kbytes, -d) unlimited scheduling priority (-e) 0 file size (blocks, -f) unlimited pending signals (-i) 15398 max locked memory (kbytes, -l) 64 max memory size (kbytes, -m) unlimited open files (-n) 1024 pipe size (512 bytes, -p) 8 POSIX message queues (bytes, -q) 819200 real-time priority (-r) 0 stack size (kbytes, -s) 8192 cpu time (seconds, -t) unlimited max user processes (-u) 15398 virtual memory (kbytes, -v) unlimited file locks (-x) unlimited
从ulimit -a的输出信息可知,本机的栈空间大小上限是8192K(8M),POSIX message queues 指可用于IPC的队列最大长度为819200B(800K)。ulimit可用于shell启动进程资源占用限制。
(2)堆是动态分配的,一般由程序员(malloc或new)分配(free或delete)释放, 若程序员不释放,由虚拟机或程序退出后由OS回收。堆空间分配会遍历空闲内存地址链表,系统为堆使用二级缓存,存取速度较慢;
操作系统有一个记录堆栈空闲内存地址的链表, 当收到程序的申请时,会从低地址向高地址遍历该链表,寻找第一个空间大于所申请空间的结点, 然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序; 大多数系统会在这块内存空间中的首地址处记录本次分配的大小, 这样代码中的delete或free语句就能够正确释放其所占的内存空间;另外, 由于找到的结点的大小不一定正好等于所申请的大小,系统会将多余的那部分重新放入空闲链表中。
堆空间不连续,尺寸上限受限于有效的虚拟内存,另外每一个应用程序通常只有一个堆(内存分配使用多个堆的情况也有)。
◊ 数据结构中的堆栈:堆指一种满足堆排序性质的数据结构,如优先队列;栈是一种先进后出的数据结构。
下图展示了Linux 系统的虚拟内存分段映射(32位系统4GB内存),用户程序将被映射到用户空间中的不同分段,拥有自己的地址空间:
A memory-mapped file is a segment of virtual memory that has been assigned a direct byte-for-bytecorrelation with some portion of a file or file-like resource. This resource is typically a file that is physicallypresent on disk, but can also be a device, shared memory object, or other resource that the operatingsystem can reference through a file descriptor. Once present, this correlation between the file and thememory space permits applications to treat the mapped portion as if it were primary memory.......A possible benefit of memory-mapped files is a "lazy loading", thus using small amounts of RAM even fora very large file. Trying to load the entire contents of a file that is significantly larger than the amount ofmemory available can cause severe thrashing as the operating system reads from disk into memory andsimultaneously writes pages from memory back to disk. Memory-mapping may not only bypass the pagefile completely, but the system only needs to load the smaller page-sized sections as data is being edited,similarly to demand paging scheme used for programs.
多核CPU与多CPU
◊ 多核CPU:指单个CPU内包含了多个核心,可分为原生多核和封装多核两种,前者抗压能力强,性能稳定。通过 cat /proc/cpuinfo 可查看本机CPU相关信息如下:
processor : 3 ... stepping : 7 microcode : 0x14 cpu MHz : 799.968 cache size : 3072 KB physical id : 0 siblings : 4 core id : 1 cpu cores : 2 ...
上表显示了本机CPU信息的一部分,processor = 3 表示本机编号3的逻辑处理器单元(当前CPU使用了超线程技术),physical id = 0 表示编号0的CPU,siblings = 4 表示当前CPU共有4个逻辑处理器单元(4核),core id = 1 表示当前CPU编号为1的物理核心,cpu cores = 2 表示本机拥有的物理核心数。
vCPU是虚拟机中对CPU的称谓,一般按照物理核心数与服务器总vCPU数的比例以1:5 ~1:10来分配较为合理。更多内容可参考 VM设计之一vCPU
◊ 多CPU:多见于企业级产品,如MPP、NUMA系统。多CPU架构最大的瓶颈就是CPU之间的I/O通讯成本。
- 多核CPU功耗低,多个CPU功耗大;
- 多核CPU是单体体积小,多个CPU多体体积大;
- 多核CPU核心之间数据交换快,多个CPU之间数据交换慢;
- 多核CPU共用内存,多个CPU各自配备专属内存。
注:超线程(Hyper-Threading,HT)把物理核心模拟成了多个逻辑处理器单元,让应用程序在同一时间里可以使用芯片的不同部分,使芯片能同时进行多个线程处理,让芯片性能得到提升;但对某些程序或未多线程编程的程序而言,它反而会降低效能。
并行(Parallellism)与并发(Concurrency)
并行如多个CPU同时执行多个任务,是真同时进行的;而并发如单个CPU通过CPU调度算法交替执行多个任务,让用户看上去是同时进行的,但实际上在即时时间点只处理单一任务。下图是 Erlang 之父 Joe Armstrong 对这个问题的形象解释:
SMP、MPP和NUMA
◊ SMP(Symmetric Multi-Processor)对称多处理器架构:
- SMP包含多个处理器(核心),每个处理器都有自己的控制单元、算术逻辑单元和寄存器;
- 每个处理器都可以通过某种互联机制(通常是系统总线)访问一个共享的主存和I/O设备,还可以通过存储器中共享地址空间中的消息和状态信息相互通信;
- SMP广泛应用于PC和移动设备领域,并行度很高,能够显著提升性能,但扩展能力有限;
- SMP系统只运行操作系统的一个拷贝,不同处理器被授权均匀访问(Uniform Memory Access,UMA亦称作统一寻址技术或统一内存存取)存储器的不同部分,但同一时间只能有一个处理器访问存储器。
◊ MPP(Massively Parallel Processing)大规模并行处理架构:
- MPP每个SMP单元都只访问本地资源(总线、内存、I/O等),彼此完全无共享(Share Nothing), 节点互联是在节点间通过 I/O 实现的(称为数据重分配Data Redistribution ,对用户是透明的);
- MPP每个SMP单元内都有操作系统和数据库的实例;
- MPP需要一种复杂的机制来调度和平衡各个节点的负载和并行处理过程,节点之间的信息交互与节点本身的处理是并行的;
- MPP扩展能力最好,理论上其扩展无限制,目前的技术可实现512个节点互联,数千个 CPU。
◊ NUMA(Non-Uniform Memory Access)非统一内存访问架构:因本地内存的访问速度远远高于远地内存的访问速度而得名
- NUMA与SMP架构类似,所有SMP节点的处理器都可以访问全系统中的物理存储器,节点互联是在同一个物理服务器内部实现的;
- NUMA采用分布式存储器模式,通过提供分离的存储器给各个处理器,避免SMP中多个处理器无法同时访问存储器的问题;
- NUMA中每个处理器访问本节点存储器所需要的时间,可能比访问其他节点存储器所花的时间要少得多;
- NUMA既保持了SMP单一操作系统拷贝、简便应用程序编程以及易于管理的特点,又继承了大规模并行处理MPP的可扩充性,可以有效地扩充系统的规模。
NUMA架构局部内存的访存延迟低于远地内存访存延迟,因此Linux采用局部结点分配策略:当一个任务请求分配内存时,首先在自己的结点内寻找空闲页,如果没有则到相邻的结点中寻找空闲页,如果还没有则到远程结点中寻找空闲页,从而在操作系统级优化了访存性能。
◊ 架构类型选择
- 数据仓库 + MPP:数据仓库环境具有大量复杂的数据处理和综合分析,要求系统具有很高的 I/O 处理能力,并且内存储系统需要提供足够的 I/O 带宽与之匹配。MPP的并行处理能力更优越,节点的 I/O 性能突出,更适合于复杂的数据综合分析与处理环境,当然需要借助于支持 MPP 技术的关系数据库系统来屏蔽节点之间负载平衡与调度的复杂性。
- OLTP + NUMA:OLTP每个交易所涉及的数据不多,要求系统具有很高的联机事务处理能力,能够在单位时间里处理尽量多的交易。而NUMA在一个物理服务器内集成许多 CPU,使系统具有较高的事务处理能力,但远地访存延时长于本地,所以要尽量减少不同节点之间的数据交互。
但这些并不是绝对的,性能的好坏由很多因素组成,单从服务器架构一个方面分析性能有一定的片面性。