深挖NUMA

http://www.litrin.net/2017/10/31/深挖numa/

首先列出本站之前相关的几篇帖子:

这次,就来深入了解下NUMA。

就如之前说的,在若干年前,对于x86架构的计算机,那时的内存控制器还没有整合进CPU,所有内存的访问都需要通过北桥芯片来完成。此时的内存访问如下图所示,被称为UMA(uniform memory access, 一致性内存访问 )。这样的访问对于软件层面来说非常容易实现:总线模型保证了所有的内存访问是一致的,不必考虑由不同内存地址之前的差异。

之后的x86平台经历了一场从“拼频率”到“拼核心数”的转变,越来越多的核心被尽可能地塞进了同一块芯片上,各个核心对于内存带宽的争抢访问成为了瓶颈;此时软件、OS方面对于SMP多核心CPU的支持也愈发成熟;再加上各种商业上的考量,x86平台也顺水推舟的搞了NUMA(Non-uniform memory access, 非一致性内存访问)。

在这种架构之下,每个Socket都会有一个独立的内存控制器IMC(integrated memory controllers, 集成内存控制器),分属于不同的socket之内的IMC之间通过QPI link通讯。

然后就是进一步的架构演进,由于每个socket上都会有多个core进行内存访问,这就会在每个core的内部出现一个类似最早SMP架构相似的内存访问总线,这个总线被称为IMC bus。

于是,很明显的,在这种架构之下,两个socket各自管理1/2的内存插槽,如果要访问不属于本socket的内存则必须通过QPI link。也就是说内存的访问出现了本地/远程(local/remote)的概念,内存的延时是会有显著的区别的。这也就是之前那篇文章中提到的为什么NUMA的设置能够明显的影响到JVM的性能。

回到当前世面上的CPU,工程上的实现其实更加复杂了。以Xeon 2699 v4系列CPU的标准来看,两个Socket之之间通过各自的一条9.6GT/s的QPI link互访。而每个Socket事实上有2个内存控制器。双通道的缘故,每个控制器又有两个内存通道(channel),每个通道最多支持3根内存条(DIMM)。理论上最大单socket支持76.8GB/s的内存带宽,而两个QPI link,每个QPI link有9.6GT/s的速率(~57.6GB/s)事实上QPI link已经出现瓶颈了。

嗯,事情变得好玩起来了。

核心数还是源源不断的增加,Skylake桌面版本的i7 EE已经有了18个core,下一代的Skylake Xeon妥妥的28个Core。为了塞进更多的core,原本核心之间类似环网的设计变成了复杂的路由。由于这种架构上的变化,导致内存的访问变得更加复杂。两个IMC也有了local/remote的区别,在保证兼容性的前提和性能导向的纠结中,系统允许用户进行更为灵活的内存访问架构划分。于是就有了“NUMA之上的NUMA”这种妖异的设定(SNC)。

回到Linux,内核mm/mmzone.c , include/linux/mmzone.h文件定义了NUMA的数据结构和操作方式。

Linux Kernel中NUMA的调度位于kernel/sched/core.c函数int sysctl_numa_balancing

  1. 在一个启用了NUMA支持的Linux中,Kernel不会将任务内存从一个NUMA node搬迁到另一个NUMA node。
  2. 一个进程一旦被启用,它所在的NUMA node就不会被迁移,为了尽可能的优化性能,在正常的调度之中,CPU的core也会尽可能的使用可以local访问的本地core,在进程的整个生命周期之中,NUMA node保持不变。
  3. 一旦当某个NUMA node的负载超出了另一个node一个阈值(默认25%),则认为需要在此node上减少负载,不同的NUMA结构和不同的负载状况,系统见给予一个延时任务的迁移——类似于漏杯算法。在这种情况下将会产生内存的remote访问。
  4. NUMA node之间有不同的拓扑结构,各个 node 之间的访问会有一个距离(node distances)的概念,如numactl -H命令的结果有这样的描述:
    node distances:
    node 0 1 2 3
     0: 10 11 21 21
     1: 11 10 21 21
     2: 21 21 10 11
     3: 21 21 11 10

可以看出:0 node 到0 node之间距离为10,这肯定的最近的距离,不提。0-1之间的距离远小于2或3的距离。这种距离方便系统在较复杂的情况下选择最合适的NUMA设定。

上图记录了某个Benchmark工具,在开启/关闭NUMA功能时QPI带宽消耗的情况。很明显的是,在开启了NUMA支持以后,QPI的带宽消耗有了两个数量级以上的下降,性能也有了显著的提升!

通常情况下,用户可以通过numactl来进行NUMA访问策略的手工配置,cgroup中cpuset.mems也可以达到指定NUMA node的作用。以numactl命令为例,它有如下策略:

  • –interleave=nodes //允许进程在多个node之间交替访问
  • –membind=nodes //将内存固定在某个node上,CPU则选择对应的core。
  • –cpunodebind=nodes //与membind相反,将CPU固定在某(几)个core上,内存则限制在对应的NUMA node之上。
  • –physcpubind=cpus //与cpunodebind类似,不同的是物理core。
  • –localalloc //本地配置
  • –preferred=node //按照推荐配置

对于某些大内存访问的应用,比如Mongodb,将NUMA的访问策略制定为interleave=all则意味着整个进程的内存是均匀分布在所有的node之上,进程可以以最快的方式访问本地内存。

讲到这里似乎已经差不多了,但事实上还没完。如果你一直按照这个“北桥去哪儿了?”的思路理下来的话,就有了另一个问题,那就是之前的北桥还有一个功能就是PCI/PCIe控制器,当然原来的南桥,现在的PCH一样也整合了PCIe控制器。事实上,在PCIe channel上也是有NUMA亲和性的。

比如:查看网卡enp0s20f0u5的NUMA,用到了netdev:<设备名称>这种方式。

[root@local ~]# numactl --prefer netdev:enp0s20f0u5 --show
policy: preferred
preferred node: 0
physcpubind: 0
cpubind: 0
nodebind: 0
membind: 0 1 2 3

或者一个PCI address 为00:17的SATA控制器,用到了pci:<PCI address>

[root@local~]# numactl --prefer pci:00:17 --show
policy: preferred
preferred node: 0
physcpubind: 0
cpubind: 0
nodebind: 0
membind: 0 1 2 3

还有block/ip/file的方式查看NUMA affinity,这里也就不再累述了。

综上,感觉上现在的服务器,特别是多socket的服务器架构更像是通过NUMA共享了内存;进而通过共享内存从而共享外设的多台主机。

最后一句:那种一个业务就能跑满整台server的时代是再也回不来了!

【转帖】深挖NUMA的更多相关文章

  1. 深挖JDK动态代理(二):JDK动态生成后的字节码分析

    接上一篇文章深挖JDK动态代理(一)我们来分析一下JDK生成动态的代理类究竟是个什么东西 1. 将生成的代理类编程一个class文件,通过以下方法 public static void transCl ...

  2. 深挖JDK动态代理(一)

     最近在研究RPC框架,避免不了的就是在RPC调用中使用最多的则是动态代理的机制了,基于此,我们先来研究一下JDK动态代理 我们先来尝试着编写一下JDK动态代理的代码 1. 由于JDK动态代理是基于接 ...

  3. 深挖Openstack Nova - Scheduler调度策略

    深挖Openstack Nova - Scheduler调度策略   一.  Scheduler的作用就是在创建实例(instance)时,为实例选择出合适的主机(host).这个过程分两步:过滤(F ...

  4. 深挖的Java源代码之Integer.parseInt()vs Integer.valueOf()

    Integer.parseInt()和Integer.valueOf()都是用来将String转换为Int的,但是为什么Java会提供两个这样的方法呢,他们如果是同样的操作,岂不是多此一举? 我们来深 ...

  5. 深挖计算机基础:MySQL实战45讲学习笔记

    参考极客时间专栏<MySQL实战45讲>学习笔记 一.基础篇(8讲) MySQL实战45讲学习笔记:第一讲 MySQL实战45讲学习笔记:第二讲 MySQL实战45讲学习笔记:第三讲 My ...

  6. 鸿蒙内核源码分析(文件句柄篇) | 深挖应用操作文件的细节 | 百篇博客分析OpenHarmony源码 | v69.01

    百篇博客系列篇.本篇为: v69.xx 鸿蒙内核源码分析(文件句柄篇) | 深挖应用操作文件的细节 | 51.c.h.o 文件系统相关篇为: v62.xx 鸿蒙内核源码分析(文件概念篇) | 为什么说 ...

  7. 深挖 NPM 机制

    使用NPM安装的时候会经常出现包冲突(比如多个主模块的子模块版本不一致等),导致在开发过程中会遇到各种或大或小的问题.所有在这会介绍以下内容: NPM 主要安装方式 NPM 包信息查询 NPM 安装机 ...

  8. python深挖65万人的明星贴吧,探究上万个帖子的秘密

    前言 本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 最近一直在关注百度明星吧,发现很多有趣的帖子,于是我就想用python把这 ...

  9. 深挖BAT内部级别和薪资待遇,你敢看?(转)

    一. 阿里内部级别和薪资待遇 先看个例子,或许你也曾像他一样纠结 BAT 的 offer: 最近刚通过阿里面试,但基本薪酬也是不升反小降. 级别只有 P6+,连 P7 都没有,非常郁闷,打算拒绝算了. ...

随机推荐

  1. 2018-2019 ACM-ICPC, China Multi-Provincial Collegiate Programming Contest

    目录 Contest Info Solutions A. Maximum Element In A Stack B. Rolling The Polygon C. Caesar Cipherq D. ...

  2. ICP、MRR、BKA等特性

    一.Index Condition Pushdown(ICP) Index Condition Pushdown (ICP)是 mysql 使用索引从表中检索行数据的一种优化方式,从mysql5.6开 ...

  3. OpenResty之ngx_lua模块的加密接口

    原文: ngx_Lua模块中的加密api接口 ngx.crc32_short digest = ngx.crc32_short(str) 该方法主要是计算给定字符串 str 的循环校验码(Cyclic ...

  4. VUE -- Identifier 'n_type' is not in camel case

    Identifier 'n_type' is not in camel case 参数名的 `_` 去掉就好了

  5. flutter中的异步机制 Future

    饿补一下Flutter中Http请求的异步操作. Dart是一个单线程语言,可以理解成物理线路中的串联,当其遇到有延迟的运算(比如IO操作.延时执行)时,线程中按顺序执行的运算就会阻塞,用户就会感觉到 ...

  6. Qt编写安防视频监控系统7-全屏切换

    一.前言 全屏切换这个功能点属于简单的,一般会做到右键菜单中,也提供了快捷键比如alt+enter来触发,恢复全屏则按esc即可,全屏处理基本上都是隐藏通道面板以外的窗体,保持最大化展示,由于采用了模 ...

  7. python 时间对比

    import datetimed1 = datetime.datetime.strptime('2015-03-05  17:41:20', '%Y-%m-%d %H:%M:%S')d2 = date ...

  8. [Scikit-learn] 1.1 Generalized Linear Models - Bayesian Ridge Regression

    1.1.10. Bayesian Ridge Regression 首先了解一些背景知识:from: https://www.r-bloggers.com/the-bayesian-approach- ...

  9. kettle的用法

    一: 从一个数据库导入表的数据到另一个 数据库的表中(表数据同步) 1:在 主对象树-- DB连接 中新建 连接: 在选项中 设置字符集: 2: 在 核心对象中 先增加一个  表输入: 再增加一个 插 ...

  10. 利用redis 分布式锁 解决集群环境下多次定时任务执行

    定时任务: @Scheduled(cron= "0 39 3 * * *") public void getAllUnSignData(){ //检查任务锁,若其它节点的相同定时任 ...