目录

代码目录结构

compile-sx.sh

compile.sh

s1.c

s2.c

s3.c

s4.c

alu.c

nop.c

alu8.c

性能测试

s1.c

s2.c

s3.c

s4.c

alu.c

nop.c

alu8.c

参考


IPC,英文全称“Instruction Per Clock”,中文翻译过来就是每个时钟的指令,即CPU每一时钟周期内所执行的指令多少,IPC代表了一款CPU的设计架构,一旦该CPU设计完成之后,IPC值就不会再改变了。在这里,IPC值的高低起到了决定性的作用,而频率似乎不再高于一切。

CPU性能判断标准公式是CPU性能=IPC(CPU每一时钟周期内所执行的指令多少)×频率(MHz时钟速度),这个公式最初由英特尔提出并被业界广泛认可的。IPC提升15%,CPU相同频率下性能提升15%,这就意味着,举个例子,如果同样是4.0GHz的主频,R7 3700比R7 2700强15%。--http://www.lotpc.com/yjzs/8463.html

一般来说IPC是越高越好, 这意味着单位时间执行了更多的指令, 通过观测IPC可以一定程度上了解软件的执行效率. 但是多高才算高呢? 这并没有标准答案, 它需要有基线进行对比, 有的代码逻辑就决定了不可能有太高的IPC, 比如存在大量的跳转逻辑或者随机访问, 当然这可能就是需要优化的地方.

代码目录结构

源码地址:https://github.com/Rtoax/test/tree/master/cpu/ipc/test-how

test-how/
├── alu8.c
├── alu.c
├── compile.sh
├── compile-sx.sh
├── nop.c
├── readme.txt
├── s1.c
├── s2.c
├── s3.c
└── s4.c

compile-sx.sh

# cat compile-sx.sh
#!/bin/bashgcc -O0 $*perf stat ./a.out

compile.sh

# cat compile.sh
#!/bin/bashgcc  $*perf stat ./a.out

s1.c

void main() {unsigned long sum = 0, i = 0;for (i = 0; i < 0x10000000; i += 1) {sum += i;}
}

s2.c

void main() {unsigned long sum = 0, a = 0, b = 0, c = 0, d = 0, i = 0;for (i = 0; i < 0x10000000; i += 4) {a += i; b += i + 1; c += i + 2; d += i + 3;}sum = a + b + c + d;
}

s3.c

void main() {unsigned long sum = 0, a = 0, b = 0, c = 0, d = 0;register unsigned long i = 0;for (i = 0; i < 0x10000000; i += 4) {a += i; b += i + 1; c += i + 2; d += i + 3;}sum = a + b + c + d;
}

s4.c

void main() {register unsigned long sum = 0, a = 0, b = 0, c = 0, d = 0;register unsigned long i = 0;for (i = 0; i < 0x10000000; i += 4) {a += i; b += i + 1; c += i + 2; d += i + 3;}sum = a + b + c + d;
}

alu.c

void main() {while(1) {__asm__ ("movq $0x0,%rax\n\t""movq $0xa,%rbx\n\t""andq $0x12345678,%rbx\n\t""orq  $0x12345678,%rbx\n\t""shlq $0x2,%rbx\n\t""addq %rbx,%rax\n\t""subq $0x14,%rax\n\t""movq %rax,%rcx");}
}

nop.c

void main() {while(1) {__asm__ ("nop\n\t""nop\n\t"/* 这里需要补齐128个nop,详情请见GitHub源码 */"nop");}
}

alu8.c

void main() {while(1) {__asm__ ("movq $0x0,%rax\n\t""movq $0xa,%rbx\n\t""andq $0x12345678,%rbx\n\t""shlq $0x2,%rbx\n\t""addq %rbx,%rax\n\t""subq $0x14,%rax\n\t""movq %rax,%rcx");}
}

性能测试

s1.c

# ./compile-sx.sh s1.c Performance counter stats for './a.out':696.193309      task-clock (msec)         #    0.998 CPUs utilized          13      context-switches          #    0.019 K/sec                  0      cpu-migrations            #    0.000 K/sec                  114      page-faults               #    0.164 K/sec                  2,325,600,151      cycles                    #    3.340 GHz                    1,345,561,852      instructions              #    0.58  insn per cycle         269,056,226      branches                  #  386.468 M/sec                  29,293      branch-misses             #    0.01% of all branches        0.697566623 seconds time elapsed

s2.c

# ./compile-sx.sh s2.c Performance counter stats for './a.out':197.554653      task-clock (msec)         #    0.997 CPUs utilized          1      context-switches          #    0.005 K/sec                  0      cpu-migrations            #    0.000 K/sec                  115      page-faults               #    0.582 K/sec                  662,040,633      cycles                    #    3.351 GHz                    1,343,510,723      instructions              #    2.03  insn per cycle         67,353,710      branches                  #  340.937 M/sec                  11,076      branch-misses             #    0.02% of all branches        0.198063870 seconds time elapsed

不过指令条数基本上没有变化, 如果再看汇编代码, 就会发现-O0编译出来的代码还有很多访存, 那么我们现在稍微修改一下, 使用register来存放变量i:

s3.c

# ./compile-sx.sh s3.c Performance counter stats for './a.out':130.640582      task-clock (msec)         #    0.996 CPUs utilized          0      context-switches          #    0.000 K/sec                  0      cpu-migrations            #    0.000 K/sec                  115      page-faults               #    0.880 K/sec                  433,319,129      cycles                    #    3.317 GHz                    1,074,802,878      instructions              #    2.48  insn per cycle         67,303,874      branches                  #  515.184 M/sec                  9,671      branch-misses             #    0.01% of all branches        0.131150296 seconds time elapsed

再进一步, 所有变量都使用register:

s4.c

# ./compile-sx.sh s4.c Performance counter stats for './a.out':64.823574      task-clock (msec)         #    0.992 CPUs utilized          1      context-switches          #    0.015 K/sec                  0      cpu-migrations            #    0.000 K/sec                  115      page-faults               #    0.002 M/sec                  215,723,116      cycles                    #    3.328 GHz                    604,800,935      instructions              #    2.80  insn per cycle         67,259,662      branches                  # 1037.580 M/sec                  8,069      branch-misses             #    0.01% of all branches        0.065371469 seconds time elapsed

到这里我们已经拿到了一个相对满意的结果, 是否还有优化的空间我们可以一起思考.

那么IPC到底说明了什么? 它从某一个侧面说明了CPU的执行效率, 却也不是全部. 想要提高应用的效率, 注意不是CPU的效率, 简单地说无非两点:

  • 没必要的事情不做
  • 必须做的事情做得更高效, 这个是IPC可以发挥的地方

既然IPC可以接近3, 那么还能不能再高点? 我们看2个测试, alu.c 和 nop.c, 测试运行在Intel(R) Xeon(R) Gold 6150 CPU @ 2.70GHz

alu.c

# ./compile.sh alu.c
^C./a.out: 中断Performance counter stats for './a.out':2338.321843      task-clock (msec)         #    0.999 CPUs utilized          14      context-switches          #    0.006 K/sec                  0      cpu-migrations            #    0.000 K/sec                  113      page-faults               #    0.048 K/sec                  7,807,833,575      cycles                    #    3.339 GHz                    28,947,455,798      instructions              #    3.71  insn per cycle         3,217,085,943      branches                  # 1375.810 M/sec                  60,083      branch-misses             #    0.00% of all branches        2.340188767 seconds time elapsed

nop.c

# ./compile.sh nop.c
^C./a.out: 中断Performance counter stats for './a.out':1556.110089      task-clock (msec)         #    0.999 CPUs utilized          5      context-switches          #    0.003 K/sec                  0      cpu-migrations            #    0.000 K/sec                  113      page-faults               #    0.073 K/sec                  5,189,621,889      cycles                    #    3.335 GHz                    18,438,724,412      instructions              #    3.55  insn per cycle         144,097,748      branches                  #   92.601 M/sec                  42,524      branch-misses             #    0.03% of all branches        1.557307312 seconds time elapsed

通过这2个测试可以看到, IPC甚至可以接近4, 同时也产生了几个疑问:

  • 3.84应该不是极限, 至少应该是个整数吧?
  • alu比nop还高, 这似乎不符合常理?
  • alu中的很多指令有依赖关系, 怎么达到高并发的?

首先来看第一个问题, 为什么是3.84, 而不是4或者5呢? 这里面第一个需要关注的地方就是while(1), 相对于其他move/and/or/shl/sub指令, 它是一个branch指令. CPU对branch的支持肯定会复杂一点, 碰到branch指令还会prefetch之后的指令吗? 如果branch taken了那之前的prefetch不就没用了? 另一个需要考虑的就是Broadwell的每个core里面只有4个ALU, 其中只有2个ALU能够执行跳转指令, 并且每个cycle最多能够dispatch 4个micro ops. 而alu.c中每个循环是8条指令, 加上跳转指令本身有9条指令, 看起来不是最好的情况. 那么在循环中减少一条指令会怎么样:

alu8.c

# ./compile.sh alu8.c
^C./a.out: 中断Performance counter stats for './a.out':2131.810701      task-clock (msec)         #    1.000 CPUs utilized          5      context-switches          #    0.002 K/sec                  0      cpu-migrations            #    0.000 K/sec                  113      page-faults               #    0.053 K/sec                  7,134,964,787      cycles                    #    3.347 GHz                    27,537,819,945      instructions              #    3.86  insn per cycle         3,442,735,496      branches                  # 1614.935 M/sec                  48,328      branch-misses             #    0.00% of all branches        2.132667345 seconds time elapsed

可以看到IPC已经达到3.99, 非常接近4了. 如果把每个循环的指令条数修改为12 (包括跳转指令), 16, 20等都可以验证IPC在3.99左右, 反之如果是13, 14就差一点. 唯一的例外来自于7, 它同样能达到3.99 (原因?), 再减少到6又差点.

参考

《IPC到底能有多高》https://zhuanlan.zhihu.com/p/138887210

Instruction Level Parallelism》http://web.cs.iastate.edu/~prabhu/Tutorial/PIPELINE/instrLevParal.html

《Instruction Level Parallelism PDF》https://eecs.ceas.uc.edu/~wilseypa/classes/eecs7095/lectureNotes/ilp/ilp.pdf

《Instruction Level Parallelism PDF》https://www.nvidia.com/content/cudazone/cudau/courses/ucdavis/lectures/ilp5.pdf

CPU的IPC调优:通过优化代码,提高每个时钟的指令数相关推荐

  1. HIVE调优(性能优化)

    HIVE调优(性能优化) HIVE调优涉及到压缩和存储调优,参数调优,sql调优,数据倾斜调优,小文件问题调优等 -1- 数据压缩和存储格式 数据压缩编码 为了支持多种压缩/解压缩算法,Hadoop引 ...

  2. 《高性能SQL调优精要与案例解析》一书谈主流关系库SQL调优(优化TUNING)技术精髓之——执行计划获取及理解

    <高性能SQL调优精要与案例解析>中,主要以Oracle为样本讲解了SQL调优(优化&TUNING),其中,前面博文中本人也说到,就SQL调优的思路.方法和步骤来说,各关系库几乎是 ...

  3. Hugging Face PEFT 调优实战附代码

    Hugging Face PEFT 调优实战附代码 PEFT调优大模型 Hugging Face PEFT 调优实战附代码 使用Hugging Face PEFT Library 先快速上手使用PEF ...

  4. Linux性能调优的优化思路

    Linux操作系统是一个开源产品,也是一个开源软件的实践和应用平台,在这个平台下有无数的开源软件支撑,我们常见的有apache.tomcat.nginx.mysql.php等等,开源软件的最大理念就是 ...

  5. python minimize_简单三步实现Python脚本超参数调优(附代码)

    作者:Jakub Czakon, Neptune.ai 翻译:陈之炎 校对:和中华 本文约1500字,建议阅读5分钟. Python机器学习模型建立起来之后,如何对它的性能进行优化?按照本指南中的三个 ...

  6. 独家 | 简单三步实现Python脚本超参数调优(附代码)

    作者:Jakub Czakon, Neptune.ai 翻译:陈之炎 校对:和中华 本文约1500字,建议阅读5分钟. Python机器学习模型建立起来之后,如何对它的性能进行优化?按照本指南中的三个 ...

  7. MySQL 性能调优和优化技巧

    介绍 MySQL 是一种流行的开源数据库应用程序,它以一种有意义且易于访问的方式存储和构造数据.对于大型应用程序,庞大的数据量可能会导致性能问题. 本指南提供了一些关于如何提高 MySQL 数据库性能 ...

  8. Linux文件系统性能调优 参数优化

    由于各种的I/O负载情形各异,Linux系统中文件系统的缺省配置一般来说都比较中庸,强调普遍适用性.然而在特定应用下,这种配置往往在I/O 性能方面不能达到最优.因此,如果应用对I/O性能要求较高,除 ...

  9. 如何调优JVM - 优化Java虚拟机(大全+实例)

    堆设置 -Xmx3550m:设置JVM最大堆内存 为3550M. -Xms3550m:设置JVM初始堆内存 为3550M.此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存. -X ...

最新文章

  1. Ubuntu Linux 安装 .7z 解压和压缩文件
  2. DEV express 对Gridview某行的元素赋值
  3. Redis:事务、管道、Lua脚本
  4. 简书的css排版,css格式化排版
  5. 从闭包函数的变量自增的角度 - 解析js垃圾回收机制
  6. 使用WildFly 8在Java EE7中自举Apache Camel
  7. linux 制作box文件夹,用busybox制作自己简易的根文件系统
  8. PHP通知弹窗代码_公告弹窗
  9. 第6集_奇点和安迪吃饭1 第一次见面
  10. Variable @link-color is undefined
  11. Arduino 电机测速
  12. dns服务器优化 360,360超级dns解析速度提升10倍
  13. 【医疗图像分割】Deep neural networks for the detection and segmentation of the retinal fluid in OCT images.
  14. 有关于win10系统不能更改自己ip得问题解决办法
  15. java 坦克大战画坦克_Java坦克大战部分:画出界面,敌人坦克,我的坦克,不出界,键盘事件【诗书画唱】...
  16. 玩客云刷armbian安装php环境_[小白版]玩客云刷armbian后搭建anmp环境+可道云
  17. Android文件解压
  18. 2.04 标志寄存器
  19. IP反查网站,ip反查接口,旁站查询接口大全,通过IP查域名汇总,域名历史解析记录查询,IP地址查对应机房名称、地址,查IP地址的AS号码...
  20. springboot美食分享系统 毕业设计-附源码612231

热门文章

  1. Spring框架----IOC的概念和作用之程序的耦合和解耦
  2. 程序设计与算法----递归汉诺塔问题
  3. 【JAVA】Maven profiles控制多环境数据源日志打包(转载)
  4. Unity UGUI实现鼠标拖动图片
  5. Java学习---JDK的安装和配置
  6. P1828 香甜的黄油 Sweet Butter (spfa)
  7. Net学习日记_ASP.Net_一般处理程序_笔记
  8. Normalize.css用法
  9. Win32 DLL和MFC DLL 中封装对话框
  10. 一个CSharp类代码,让你的窗体显示的更酷(转)