性能优化:来龙及去脉

背景

这是我在离开Juniper之前应manager及同事们的要求,给他们做的一个培训。 因为我是结合Juniper具体产品来写的材料,所以这里就删去了Juniper的关键信息。

什么是性能优化

  • 性能优化是一个理论体系,然而终究是要去解决实际问题.

    • 所以,有效的才是最好的!
  • 要去解决实际问题,终归还是得有理论基础

    • Profiling Tools
    • 可执行文件
      • 文件格式
      • 编译基础:gcc
      • 加载执行
    • CPU体系结构
      • CPU performance counter registers
      • Cache
    • 内存管理
      • Kernel: Page
      • 多线程: 共享内存
      • Glibc: 堆内存管理算法
    • 剩下的,都是编程语言的事了

Profiling Tools

  • 开发者在写代码时就意识到了这个函数对性能影响较大
    • 所以我们自己在这些函数的入口和出口分别获取一些统计计数,就可以计算出这个函数的执行开销。
    • 这样的缺点也是显而易见,用这种方法只能看这些特定的函数
  • 我们还想看其他函数的开销怎么办?
    • 第一种策略是受限于编译时,所以只能看特定的函数,那是不是可以在运行时再去决定要profile的函数?
    • 于是就有了另外一种profiling策略:首先这个函数必须得是全局符号就,然后根据这个全局符号来找到它的地址,接着把这个地址里的指令替换为我们自己的指令跳转到统计函数里,统计完后再返回原来的函数继续去执行
    • 这样做显然也是有局限性的,那就是只能profile全局符号
  • 那编译时可不可以做到来profile所有的函数?
    • 答案是有的。比如gprof这个工具,它在编译时会对所有全局函数的入口和出口处做统计。

关于profiling tools的一些思考

  • 要不要把profiling tool编译进最终的可执行文件?
    • 如果要是编译进去,显然会影响性能
    • 如果不。那么那么实际上这是两个不同的可执行文件,所以结果并不能真实的反应实际结果
  • 可不可以退而求其次,把它给编译进去,但是我们通过一个开关来控制它,默认关闭。
    • 看起来,这相对于不编译进去,在运行时只是多了一个if的判断指令,一条判断语句也费不了多少时间。
    • 然而,这些代码被编译为二进制后还是要占用地址空间的,从而会影响到指令预取。
    • 那么,我们使用unlikely把这部分代码给放在编译到函数的最后面不就可以不影响指令预取了么?是的,但是它只是不影响这个函数内的指令预取,从全局来看,指令预取还是会受到影响。
  • 结论:鱼与熊掌不可兼得,要么要性能要么要准确性

涉及到的可执行文件的一些知识

Comments