在摩尔定律失效的情况下,CPU开端通过多核来进步功用,但这带来了一系列的问题,本系列的文章将介绍这些问题、在X86下现在的解决方案、以及咱们应该怎样更好的运用CPU。
主张保藏,本文将继续更新~ 前文: 浅谈NUMA架构[一]-CPU架构演进过程 – () 浅谈NUMA架构[二]-NUMA下CPU拜访内存整体流程 – ()
一、CPU运用优化
怎样评估是否系统瓶颈在CPU?
首要需求观察CPU运用情况,关于CPU idle、Top指令的CPU%、perf的ipc(Instruction per cycle),这三者得到的数据共同吗?
Top和idle的方针会运用CPU处于非idle状况下的时间 / 整体时间 核算CPU运用百分比,这在必定程度上能反映CPU的状况,但是却不能给CPU调优供给更多的信息了;
关于perf的ipc方针,会考虑到CPU处于等候数据从Memory加载到Cache的耗时(CPU Install),一般情况下(cpu wide = 4),关于多核系统:
- IPC < 1,当时系统倾向处于Memory Bound,此刻即便再增加CPU的作用关于系统功用的进步也是有限的;
- IPC > 1,当时系统倾向处于CPU Instructions Bound,即CPU处在相对较高的运行负荷上。
NUMA下怎样优化
Memory Bound
关于Memory Bound,在内存带宽必定的情况下优先考虑:
- 优化代码规划,减少不必要的Memory IO;常见的办法比如:数据库的列式存储,关于Count()、Max()等列式聚合查询,比较于行式存储需求将悉数完好行数据从db->mem->cache,而列式只需求将完好列进行load即可,关于内存带宽的压力减少非常多,而CPU处在Memory Stall的时间天然也会下降。
-
优化Cache的命中率,因为L2比较于Memory可以节省约230cycle;常见的比如:代码Cache和睦:缓存行填充(Disruptor),不过这种办法比较hardcode;另一种常见的办法比如:在一个CacheLine中可以存储数组中的多个值时,数组按行遍历比较于按列遍历是Cache和睦的,在数据量和拜访量较大时,功用会有50%以上的进步;比较于缓存行有更新原因不同的多种数据导致的Cache回写,在NUMA下更常见的是因为线程切核导致的Cache命中率下降,因此可以通过
taskset
或许通过Java结构Affinity
修正线程对核的亲和性来防止线程搬家核心实行,不过绑核的前提是需求认知不同CPU使命关于CPU资源的消耗程度,尽量使每个核上绑定的线程实行的使命量相近;
CPU Bound
关于CPU Bound,在CPU核心数、clock rate不变的情况下优先考虑:
- 使得一个CPU cycle可以处理更多的内存数据,常见的办法比如SIMD(Single Instruction Multiple Data),在Java9中关于String.indexOf()办法现已开端测验运用SIMD进行字符串查找,比较于传统的for循环单字符匹配,功用要好上不少。
- 优化代码规划,减少不必要的CPU运算,在事务系统中常见的比如Json全量序列化在只取部分字段时,是否可以改为流式的获取办法;比如将常常重复核算的核算成果放到映射表中去;
- 优化线程模型,减少不必要的线程数 ,CFS调度器会确保每一个安排稳当线程在一次调度周期中会得到调度,线程越多,线程切换越多,每个调度周期便越长,保护本钱也越高。
二、首要参考
- CPU方针、功用优化相关:CPU Utilization is Wrong (brendangregg.com)、一篇论文讲透Cache优化 – 知乎 (zhihu.com)