软件性能调优:看数据,还是谈概念?

上周写了「想让服务器跑得快,并不是换个编程语言那么简单」,很多朋友的留言歪了楼:论性能,C语言甩Python数倍到数十倍,你说和编程语言没关?拜托,程序君只是说,不是换个编程语言那么简单。这种留言,我都是建议看了原文再说。还有朋友留言说她转到朋友圈里炸了锅,大家众口一词:不谈zero copy,谈什么服务器性能!优化系统调用有个毛用!

对此,我只能说,buzz word荼毒太深,以至于大家陷进去,遇到问题,先摆个伟光正的大道理出来。我想,很多开口闭口zero copy的主,也许连zero copy是何物都没搞清楚。

我们看wikipeidia上的定义:

"Zero-copy" describes computer operations in which the CPU does not perform the task of copying data from one memory area to another. This is frequently used to save CPU cycles and memory bandwidth when transmitting a file over a network.

很清楚,zero-copy是当数据需要从内存的A点拷到B的时候,CPU不参与这个过程,也就是不通过调用 memcpy(),实现内存拷贝。这个过程一般是device driver来完成,通过DMA(direct memory access)绕过CPU在外设(一般是NIC,也就是网卡)和主存中做乾坤大挪移。放一张图:

至于DMA是怎么工作的,怎么发起,怎么通知CPU,cache coherence怎么处理,这就不是本文要讲的内容。

当然,这是狭义的zero copy,一般更广义的zero copy还包括在DMA结束后的整个数据的生存周期里,数据尽可能地少拷贝(绝对不是不拷贝哦)。

我们看Kernel如何收包(简化版):

  • 驱动初始化时,为NIC分配ring buffer。
  • NIC收到报文后,找到下一个空闲的buffer,将其DMA到buffer指定的内存里,同时发送interrupt,告知Kernel。(第一次zero copy)
  • Kernel处理这个interrupt,将这个buffer据为己有做后续处理,然后新分配一个buffer,还给driver。(第二次zero copy)。
  • 接下来交给TCP stack处理(因为咱们讲的是服务器开发,一般是TCP服务器)。TCP是个stream based protocol,在这里,报文的排序,组包(把TCP头去掉,payload连接起来供application使用)等等,根据实现的不同,也许需要copy至少一次。另外,如果自己作为发送端,由于需要考虑潜在的retransmit,一般也会copy一份到retransmit queue里。(网络设备专门优化不在此讨论范围之内)
  • 之后交给application处理,这涉及到kernel space到user space的切换。将application的buffer和kernel的buffer映射起来不是一件简单的事情,所以这里一般也会有一次copy。

当然,现在有跳过kernel的stack,从NIC直接DMA到user space的技术,但那一般是网络厂商干的勾当。我觉得咱们做一个服务器软件,还是不要抢人家TCP stack的生意,否则你会把自己玩死。

基本上,这些动作都发生在application无法控制的kernel里。好处是:当你 recv() 的时候,你拿到了一个只包含有 TCP payload 的 重组好的,可以线性阅读的buffer。你只需要关心application level的逻辑。你能控制的,也只有之后,尽可能少的copy这个buffer。嗯,我们兜了这么一大圈,终于回到之前的问题:不谈zero copy,谈什么服务器性能,谈什么优化系统调用!

无奈的是,我们只能控制application level的zero copy。

假设我们写了一个 HTTP server。大多数HTTP请求头都不会太大,除非是post一个文件,或者应用服务器设置了巨大的cookie。我们假设平均而言整个请求大小是4k(这个假设没有价值,只是为了比对),而我们的 server 稍稍2b一些,除了正常处理中的copy之外,还会把整个请求 memmcpy() 一遍。为公平起见,我们不和那些占用时间比较长的系统调用比较,就和 gettimeofday() 比一比。主体代码如下:

(顺手写下的两行代码,如有纰漏,敬请原谅)

带着gprof编译运行,然后查看看结果:

(注意:这个结果你最好自己写代码测一下,不同的环境可能不一样,我用的是digital ocean最小的instance,ubuntu 14.04)

哟,貌似 memcpy() 没有想象的那么慢嘛,同样10million调用次数,比 gettimeofday() 慢一点点而已。

那问题来了,你是费尽心思去优化散落在各处小小的,基本上不可避免的copy呢,还是1s调用一次 gettimeofday(),而不是来一个包就调用一次,省却99.99%的调用呢?

你是会把发送response时分别两次发送header和body的两个 write() 合并成一个,来减少几十ms级的网络round trip的延迟,还是费尽心思去优化散落的copy呢?

你是会通过使用 stracegprof,以及 systemtap 等各种工具,追溯到真正性能所在的瓶颈,然后对症下药,还是不假思索地跳将出来:一切不谈zero-copy而论performance的服务器软件都是耍流氓!

当你看不起系统调用带来的损耗时,你是否又知道,当你苦苦追寻zero-copy的时候,kernel已经尽力在提供各种扩充的系统调用来尽可能让某些应用场景快起来?比如 sendfile()?如果你的response是个静态文件,你可以通过这个系统调用轻松实现zero-copy?

写这么些,不是证明我有多对,我的知识也有可能是错的。只是当我们遇到问题的时候,是真正测量还是人云亦云,吐几个buzz word就自认为解决问题了呢?

至少,我写上一篇文章的时候,我还拿strace亲测了一下nginx在首次访问和再次访问同一URI下系统调用的不同呢?

不管你怎么看待题图中的人物,但我喜欢:「我不是为了输赢,我只是认真」这句话。performance是认认真真不断测量和调整打造出来的,不是拍脑门想出来的。

原文发布于微信公众号 - 程序人生(programmer_life)

原文发表时间:2015-07-27

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏FreeBuf

微软联手英特尔,在Windows更新中推送Sepctre微代码升级

微软今天表示,将采用英特尔的 CPU 微码更新,修复 Spectre v2 漏洞,并通过 Windows 更新包将这些微代码更新发送给用户。 ? 2018 年年...

33370
来自专栏别先生

一脸懵逼学习Hadoop-HA机制(以及HA机制的配置文件,测试)

1:能否让两个NameNode都正常影响客户端请求?   应该让两个NameNode节点在某个时间只能有一个节点正常影响客户端请求,相应请求的必须为Active...

24860
来自专栏宏伦工作室

基于itchat实现微信群消息同步机器人

53770
来自专栏Java架构

JAVA程序员怎么样才能进一线互联网公司

2.做过哪些项目?项目中遇到哪些难点,你是怎样解决的?单点登录系统说一下?分布式缓存的使用场景?(说好的基础呢,上来就是项目,毫无准备,导致好多东西都记不起来了...

21920
来自专栏FreeBuf

挖洞经验 | 看我如何利用上传漏洞在PayPal服务器上实现RCE执行

当你看到这篇文章标题时,是不是很吃惊,PayPal服务器的RCE漏洞?Dafaq?WTF?真的吗?这当然是真的,很幸运,我通过枚举和域名查找方法发现了该漏洞。 ...

40750
来自专栏Golang语言社区

go语言项目优化(经验之谈)

我的课题主要分为以下三章,斗鱼在GO的应用场景,GO在业务中如何优化,我们在GO中踩过了哪些坑。

26230
来自专栏野路子程序员

徒手解剖composer,简单了解其实现过程

31260
来自专栏腾讯架构师的专栏

云计算时代的数据库核弹头 : Tencent MySQL ( TXSQL)

作为腾讯规模最大的 MySQL 数据库服务,CDB 在腾讯云上也是最受欢迎的关系型数据库产品。CDB 不仅具备备份回档、监控、快速扩容等数据库运维的全套解决方案...

76200
来自专栏数据和云

经典文档:Oracle Database 12.2新特性概览解读下载

在2017 OOW大会上,关于Oracle Database 12.2 数据库的新特性介绍仍然引人瞩目,会后公布了 Oracle VP Swonger的文档,我...

33270
来自专栏Java3y

图书管理系统【总结】

感想 该项目是目前为止,我写过代码量最多的项目了.....虽然清楚是没有含金量的【跟着视频来写的】,但感觉自己也在进步中...... 写的过程中,出了不少的问题...

41050

扫码关注云+社区

领取腾讯云代金券