标签 性能 下的文章

cpustat 是 Linux 下一个强大的系统性能测量程序,它用 Go 编程语言 编写。它通过使用 “用于分析任意系统的性能的方法(USE)”,以有效的方式显示 CPU 利用率和饱和度。

它高频率对系统中运行的每个进程进行取样,然后以较低的频率汇总这些样本。例如,它能够每 200ms 测量一次每个进程,然后每 5 秒汇总这些样本,包括某些度量的最小/平均/最大值(min/avg/max)。

推荐阅读: 监控 Linux 性能的 20 个命令行工具

cpustat 能用两种方式输出数据:定时汇总的纯文本列表和每个取样的彩色滚动面板。

如何在 Linux 中安装 cpustat

为了使用 cpustat,你的 Linux 系统中必须安装有 Go 语言(GoLang),如果你还没有安装它,点击下面的链接逐步安装 GoLang:

安装完 Go 以后,输入下面的 go get 命令安装 cpustat,这个命令会将 cpustat 二进制文件安装到你的 GOBIN 变量(所指的路径):

# go get github.com/uber-common/cpustat

如何在 Linux 中使用 cpustat

安装过程完成后,如果你不是以 root 用户控制系统,像下面这样使用 sudo 命令获取 root 权限运行 cpustat,否则会出现下面显示的错误信息:

$ $GOBIN/cpustat
This program uses the netlink taskstats interface, so it must be run as root.

注意:想要像你系统中已经安装的其它 Go 程序那样运行 cpustat,你需要把 GOBIN 变量添加到 PATH 环境变量。打开下面的链接学习如何在 Linux 中设置 PATH 变量。

cpustat 是这样工作的:在每个时间间隔查询 /proc 目录获取当前进程 ID 列表,然后:

  • 对于每个 PID,读取 /proc/pid/stat,然后计算和前一个样本的差别。
  • 如果是一个新的 PID,读取 /proc/pid/cmdline
  • 对于每个 PID,发送 netlink 消息获取 taskstat,计算和前一个样本的差别。
  • 读取 /proc/stat 获取总的系统统计信息。

根据获取所有这些统计信息所花费的时间,会调整每个休息间隔。另外,通过每次取样之间实际经过的时间,每个样本也会记录它用于测量的时间。这可用于计算 cpustat 自身的延迟。

当不带任何参数运行时,cpustat 默认会显示以下信息:样本间隔:200ms;汇总间隔:2s(10 个样本);显示前 10 个进程;用户过滤器:all;pid 过滤器:all。正如下面截图所示:

$ sudo $GOBIN/cpustat 

cpustat - 监控 Linux CPU 使用

cpustat – 监控 Linux CPU 使用

在上面的输出中,之前显示的系统范围的度量字段意义如下:

  • usr - 用户模式运行时间占 CPU 百分比的 min/avg/max 值。
  • sys - 系统模式运行时间占 CPU 百分比的 min/avg/max 值。
  • nice - 用户模式低优先级运行时间占 CPU 百分比的 min/avg/max 值。
  • idle - 用户模式空闲时间占 CPU 百分比的 min/avg/max 值。
  • iowait - 等待磁盘 IO 的 min/avg/max 延迟时间。
  • prun - 处于可运行状态的 min/avg/max 进程数量(同“平均负载”一样)。
  • pblock - 被磁盘 IO 阻塞的 min/avg/max 进程数量。
  • pstat - 在本次汇总间隔里启动的进程/线程数目。

同样还是上面的输出,对于一个进程,不同列的意思分别是:

  • name - 从 /proc/pid/stat/proc/pid/cmdline 获取的进程名称。
  • pid - 进程 ID,也被用作 “tgid” (线程组 ID)。
  • min - 该 pid 的用户模式+系统模式时间的最小样本,取自 /proc/pid/stat。比率是 CPU 的百分比。
  • max - 该 pid 的用户模式+系统模式时间的最大样本,取自 /proc/pid/stat
  • usr - 在汇总期间该 pid 的平均用户模式运行时间,取自 /proc/pid/stat
  • sys - 在汇总期间该 pid 的平均系统模式运行时间,取自 /proc/pid/stat
  • nice - 表示该进程的当前 “nice” 值,取自 /proc/pid/stat。值越高表示越好(nicer)。
  • runq - 进程和它所有线程可运行但等待运行的时间,通过 netlink 取自 taskstats。比率是 CPU 的百分比。
  • iow - 进程和它所有线程被磁盘 IO 阻塞的时间,通过 netlink 取自 taskstats。比率是 CPU 的百分比,对整个汇总间隔平均。
  • swap - 进程和它所有线程等待被换入(swap in)的时间,通过 netlink 取自 taskstats。Scale 是 CPU 的百分比,对整个汇总间隔平均。
  • vcx 和 icx - 在汇总间隔期间进程和它的所有线程自动上下文切换总的次数,通过 netlink 取自 taskstats。
  • rss - 从 /proc/pid/stat 获取的当前 RSS 值。它是指该进程正在使用的内存数量。
  • ctime - 在汇总间隔期间等待子进程退出的用户模式+系统模式 CPU 时间总和,取自 /proc/pid/stat。 注意长时间运行的子进程可能导致混淆这个值,因为只有在子进程退出后才会报告时间。但是,这对于计算高频 cron 任务以及 CPU 时间经常被多个子进程使用的健康检查非常有帮助。
  • thrd - 汇总间隔最后线程的数目,取自 /proc/pid/stat
  • sam - 在这个汇总间隔期间该进程的样本数目。最近启动或退出的进程可能看起来比汇总间隔的样本数目少。

下面的命令显示了系统中运行的前 10 个 root 用户进程:

$ sudo $GOBIN/cpustat -u root

查找 root 用户正在运行的进程

查找 root 用户正在运行的进程

要想用更好看的终端模式显示输出,像下面这样用 -t 选项:

$ sudo $GOBIN/cpustat -u root -t

root 用户正在运行的进程

root 用户正在运行的进程

要查看前 x 个进程(默认是 10),你可以使用 -n 选项,下面的命令显示了系统中 正在运行的前 20 个进程

$ sudo $GOBIN/cpustat -n 20 

你也可以像下面这样使用 -cpuprofile 选项将 CPU 信息写到文件,然后用 cat 命令查看文件:

$ sudo $GOBIN/cpustat -cpuprofile cpuprof.txt
$ cat cpuprof.txt

要显示帮助信息,像下面这样使用 -h 选项:

$ sudo $GOBIN/cpustat -h

可以从 cpustat 的 Github 仓库:https://github.com/uber-common/cpustat 查阅其它资料。

就是这些!在这篇文章中,我们向你展示了如何安装和使用 cpustat,Linux 下的一个有用的系统性能测量工具。通过下面的评论框和我们分享你的想法吧。


作者简介:

Aaron Kili 是一个 Linux 和 F.O.S.S(Free and Open-Source Software) 爱好者,一个 Linux 系统管理员、web 开发员,现在也是 TecMint 的内容创建者,他喜欢和电脑一起工作,他相信知识共享。


via: http://www.tecmint.com/cpustat-monitors-cpu-utilization-by-processes-in-linux/

作者:Aaron Kili 译者:ictlyh 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

pyDash 是一个轻量且基于 web 的 Linux 性能监测工具,它是用 Python 和 Django 加上 Chart.js 来写的。经测试,在下面这些主流 Linux 发行版上可运行:CentOS、Fedora、Ubuntu、Debian、Raspbian 以及 Pidora 。

你可以使用这个工具来监视你的 Linux 个人电脑/服务器资源,比如 CPU、内存、网络统计,包括在线用户的进程以及更多。仪表盘完全由主要的 Python 发行版本所提供的 Python 库开发,因此它的依赖关系很少,你不需要安装许多包或库来运行它。

在这篇文章中,我将展示如何安装 pyDash 来监测 Linux 服务器性能。

如何在 Linux 系统下安装 pyDash

1、首先,像下面这样安装需要的软件包 gitPython pip

-------------- 在 Debian/Ubuntu 上 --------------
$ sudo apt-get install git python-pip
-------------- 在 CentOS/RHEL 上 --------------
# yum install epel-release
# yum install git python-pip
-------------- 在 Fedora 22+ 上 --------------
# dnf install git python-pip

2、如果安装好了 git 和 Python pip,那么接下来,像下面这样安装 virtualenv,它有助于处理针对 Python 项目的依赖关系:

# pip install virtualenv
或
$ sudo pip install virtualenv

3、现在,像下面这样使用 git 命令,把 pyDash 仓库克隆到 home 目录中:

# git clone https://github.com/k3oni/pydash.git
# cd pydash

4、下一步,使用下面的 virtualenv 命令为项目创建一个叫做 pydashtest 虚拟环境:

$ virtualenv pydashtest #give a name for your virtual environment like pydashtest

Create Virtual Environment

创建虚拟环境

重要:请注意,上面的屏幕截图中,虚拟环境的 bin 目录被高亮显示,你的可能和这不一样,取决于你把 pyDash 目录克隆到什么位置。

5、创建好虚拟环境(pydashtest)以后,你需要在使用前像下面这样激活它:

$ source /home/aaronkilik/pydash/pydashtest/bin/activate

Active Virtual Environment

激活虚拟环境

从上面的屏幕截图中,你可以注意到,提示字符串 1(PS1)已经发生改变,这表明虚拟环境已经被激活,而且可以开始使用。

6、现在,安装 pydash 项目 requirements;如何你好奇的话,可以使用 cat 命令查看 requirements.txt 的内容,然后像下面所示那样进行安装:

$ cat requirements.txt
$ pip install -r requirements.txt

7、现在,进入 pydash 目录,里面包含一个名为 settings.py 的文件,也可直接运行下面的命令打开这个文件,然后把 SECRET_KEY 改为一个特定值:

$ vi pydash/settings.py

Set Secret Key

设置密匙

保存文件然后退出。

8、之后,运行下面的命令来创建一个项目数据库和安装 Django 的身份验证系统,并创建一个项目的超级用户:

$ python manage.py syncdb

根据你的情况回答下面的问题:

Would you like to create one now? (yes/no): yes
Username (leave blank to use 'root'): admin
Email address: [email protected]
Password: ###########
Password (again): ############

Create Project Database

创建项目数据库

9、这个时候,一切都设置好了,然后,运行下面的命令来启用 Django 开发服务器:

$ python manage.py runserver

10、接下来,打开你的 web 浏览器,输入网址:http://127.0.0.1:8000/ 进入 web 控制台登录界面,输入你在第 8 步中创建数据库和安装 Django 身份验证系统时创建的超级用户名和密码,然后点击登录。

pyDash Login Interface

pyDash 登录界面

11、登录到 pydash 主页面以后,你将会可以看到监测系统的基本信息,包括 CPU、内存和硬盘使用量以及系统平均负载。

向下滚动便可查看更多部分的信息。

pyDash Server Performance Overview

pydash 服务器性能概述

12、下一个屏幕截图显示的是一段 pydash 的跟踪界面,包括 IP 地址、互联网流量、硬盘读/写、在线用户以及 netstats 。

pyDash Network Overview

pyDash 网络概述

13、下一个 pydash 主页面的截图显示了一部分系统中被监视的活跃进程。

pyDash Active Linux Processes

pyDash 监视活跃 Linux 进程

如果想了解更多信息,请在 GitHub 上查看 pydash:https://github.com/k3oni/pydash

这就是全部内容了。在这篇文章中,我们展示了在 Linux 中如何安装 pyDash 并测试它的主要特性。如果你有什么想法,可以通过下面的反馈部分联系我们;如果你知道任何有用或类似的工具,也可以在评论中告知我们。


作者简介:

我叫 Ravi Saive,是 TecMint 的原创作者,是一个喜欢在网上分享技巧和知识的计算机极客和 Linux Guru。我的大多数服务器都运行在 Linux 开源平台上。请关注我:TwitterFacebook 以及 Google+


via: http://www.tecmint.com/pydash-a-web-based-linux-performance-monitoring-tool/

作者:Ravi Saive 译者:ucasFL 校对:jasminepeng

本文由 LCTT 原创编译,Linux中国 荣誉推出

为牺牲性能追求生产率而呐喊

让我从关于 Python 中的 asyncio 这个标准库的讨论中休息一会,谈谈我最近正在思考的一些东西:Python 的速度。对不了解我的人说明一下,我是一个 Python 的粉丝,而且我在我能想到的所有地方都积极地使用 Python。人们对 Python 最大的抱怨之一就是它的速度比较慢,有些人甚至拒绝尝试使用 Python,因为它比其他语言速度慢。这里说说为什么我认为应该尝试使用 Python,尽管它是有点慢。

速度不再重要

过去的情形是,程序需要花费很长的时间来运行,CPU 比较贵,内存也很贵。程序的运行时间是一个很重要的指标。计算机非常的昂贵,计算机运行所需要的电也是相当贵的。对这些资源进行优化是因为一个永恒的商业法则:

优化你最贵的资源。

在过去,最贵的资源是计算机的运行时间。这就是导致计算机科学致力于研究不同算法的效率的原因。然而,这已经不再是正确的,因为现在硅芯片很便宜,确实很便宜。运行时间不再是你最贵的资源。公司最贵的资源现在是它的员工时间。或者换句话说,就是你。把事情做完比把它变快更加重要。实际上,这是相当的重要,我将把它再次放在这里,仿佛它是一个引文一样(给那些只是粗略浏览的人):

把事情做完比快速地做事更加重要。

你可能会说:“我的公司在意速度,我开发一个 web 应用程序,那么所有的响应时间必须少于 x 毫秒。”或者,“我们失去了客户,因为他们认为我们的 app 运行太慢了。”我并不是想说速度一点也不重要,我只是想说速度不再是最重要的东西;它不再是你最贵的资源。

速度是唯一重要的东西

当你在编程的背景下说 速度 时,你通常是说性能,也就是 CPU 周期。当你的 CEO 在编程的背景下说 速度 时,他指的是业务速度,最重要的指标是产品上市的时间。基本上,你的产品/web 程序是多么的快并不重要。它是用什么语言写的也不重要。甚至它需要花费多少钱也不重要。在一天结束时,让你的公司存活下来或者死去的唯一事物就是产品上市时间。我不只是说创业公司的想法 -- 你开始赚钱需要花费多久,更多的是“从想法到客户手中”的时间期限。企业能够存活下来的唯一方法就是比你的竞争对手更快地创新。如果在你的产品上市之前,你的竞争对手已经提前上市了,那么你想出了多少好的主意也将不再重要。你必须第一个上市,或者至少能跟上。一但你放慢了脚步,你就输了。

企业能够存活下来的唯一方法就是比你的竞争对手更快地创新。

一个微服务的案例

像 Amazon、Google 和 Netflix 这样的公司明白快速前进的重要性。他们创建了一个业务系统,可以使用这个系统迅速地前进和快速的创新。微服务是针对他们的问题的解决方案。这篇文章不谈你是否应该使用微服务,但是至少要理解为什么 Amazon 和 Google 认为他们应该使用微服务。

微服务本来就很慢。微服务的主要概念是用网络调用来打破边界。这意味着你正在把使用的函数调用(几个 cpu 周期)转变为一个网络调用。没有什么比这更影响性能了。和 CPU 相比较,网络调用真的很慢。但是这些大公司仍然选择使用微服务。我所知道的架构里面没有比微服务还要慢的了。微服务最大的弊端就是它的性能,但是最大的长处就是上市的时间。通过在较小的项目和代码库上建立团队,一个公司能够以更快的速度进行迭代和创新。这恰恰表明了,非常大的公司也很在意上市时间,而不仅仅只是只有创业公司。

CPU 不是你的瓶颈

如果你在写一个网络应用程序,如 web 服务器,很有可能的情况会是,CPU 时间并不是你的程序的瓶颈。当你的 web 服务器处理一个请求时,可能会进行几次网络调用,例如到数据库,或者像 Redis 这样的缓存服务器。虽然这些服务本身可能比较快速,但是对它们的网络调用却很慢。这里有一篇很好的关于特定操作的速度差异的博客文章。在这篇文章里,作者把 CPU 周期时间缩放到更容易理解的人类时间。如果一个单独的 CPU 周期等同于 1 秒,那么一个从 California 到 New York 的网络调用将相当于 4 年。那就说明了网络调用是多少的慢。按一些粗略估计,我们可以假设在同一数据中心内的普通网络调用大约需要 3 毫秒。这相当于我们“人类比例” 3 个月。现在假设你的程序是高 CPU 密集型,这需要 100000 个 CPU 周期来对单一调用进行响应。这相当于刚刚超过 1 天。现在让我们假设你使用的是一种要慢 5 倍的语言,这将需要大约 5 天。很好,将那与我们 3 个月的网络调用时间相比,4 天的差异就显得并不是很重要了。如果有人为了一个包裹不得不至少等待 3 个月,我不认为额外的 4 天对他们来说真的很重要。

上面所说的终极意思是,尽管 Python 速度慢,但是这并不重要。语言的速度(或者 CPU 时间)几乎从来不是问题。实际上谷歌曾经就这一概念做过一个研究,并且他们就此发表过一篇论文。那篇论文论述了设计高吞吐量的系统。在结论里,他们说到:

在高吞吐量的环境中使用解释性语言似乎是矛盾的,但是我们已经发现 CPU 时间几乎不是限制因素;语言的表达性是指,大多数程序是源程序,同时它们的大多数时间花费在 I/O 读写和本机的运行时代码上。而且,解释性语言无论是在语言层面的轻松实验还是在允许我们在很多机器上探索分布计算的方法都是很有帮助的,

再次强调:

CPU 时间几乎不是限制因素。

如果 CPU 时间是一个问题怎么办?

你可能会说,“前面说的情况真是太好了,但是我们确实有过一些问题,这些问题中 CPU 成为了我们的瓶颈,并造成了我们的 web 应用的速度十分缓慢”,或者“在服务器上 X 语言比 Y 语言需要更少的硬件资源来运行。”这些都可能是对的。关于 web 服务器有这样的美妙的事情:你可以几乎无限地负载均衡它们。换句话说,可以在 web 服务器上投入更多的硬件。当然,Python 可能会比其他语言要求更好的硬件资源,比如 c 语言。只是把硬件投入在 CPU 问题上。相比于你的时间,硬件就显得非常的便宜了。如果你在一年内节省了两周的生产力时间,那将远远多于所增加的硬件开销的回报。

那么,Python 更快一些吗?

这一篇文章里面,我一直在谈论最重要的是开发时间。所以问题依然存在:当就开发时间而言,Python 要比其他语言更快吗?按常规惯例来看,我、google 还有其他几个人可以告诉你 Python 是多么的高效。它为你抽象出很多东西,帮助你关注那些你真正应该编写代码的地方,而不会被困在琐碎事情的杂草里,比如你是否应该使用一个向量或者一个数组。但你可能不喜欢只是听别人说的这些话,所以让我们来看一些更多的经验数据。

在大多数情况下,关于 python 是否是更高效语言的争论可以归结为脚本语言(或动态语言)与静态类型语言两者的争论。我认为人们普遍接受的是静态类型语言的生产力较低,但是,这有一篇优秀的论文解释了为什么不是这样。就 Python 而言,这里有一项研究,它调查了不同语言编写字符串处理的代码所需要花费的时间,供参考。

在上述研究中,Python 的效率比 Java 高出 2 倍。有一些其他研究也显示相似的东西。 Rosetta Code 对编程语言的差异进行了深入的研究。在论文中,他们把 python 与其他脚本语言/解释性语言相比较,得出结论:

Python 更简洁,即使与函数式语言相比较(平均要短 1.2 到 1.6 倍)

普遍的趋势似乎是 Python 中的代码行总是更少。代码行听起来可能像一个可怕的指标,但是包括上面已经提到的两项研究在内的多项研究表明,每种语言中每行代码所需要花费的时间大约是一样的。因此,限制代码行数就可以提高生产效率。甚至 codinghorror(一名 C# 程序员)本人写了一篇关于 Python 是如何更有效率的文章

我认为说 Python 比其他的很多语言更加的有效率是公正的。这主要是由于 Python 有大量的自带以及第三方库。这里是一篇讨论 Python 和其他语言间的差异的简单的文章。如果你不知道为何 Python 是如此的小巧和高效,我邀请你借此机会学习一点 python,自己多实践。这儿是你的第一个程序:

import __hello__ 

但是如果速度真的重要呢?

上述论点的语气可能会让人觉得优化与速度一点也不重要。但事实是,很多时候运行时性能真的很重要。一个例子是,你有一个 web 应用程序,其中有一个特定的端点需要用很长的时间来响应。你知道这个程序需要多快,并且知道程序需要改进多少。

在我们的例子中,发生了两件事:

  1. 我们注意到有一个端点执行缓慢。
  2. 我们承认它是缓慢,因为我们有一个可以衡量是否足够快的标准,而它没达到那个标准。

我们不必在应用程序中微调优化所有内容,只需要让其中每一个都“足够快”。如果一个端点花费了几秒钟来响应,你的用户可能会注意到,但是,他们并不会注意到你将响应时间由 35 毫秒降低到 25 毫秒。“足够好”就是你需要做到的所有事情。免责声明: 我应该说有一些应用程序,如实时投标程序,确实需要细微优化,每一毫秒都相当重要。但那只是例外,而不是规则。

为了明白如何对端点进行优化,你的第一步将是配置代码,并尝试找出瓶颈在哪。毕竟:

任何除了瓶颈之外的改进都是错觉。 Any improvements made anywhere besides the bottleneck are an illusion. -- Gene Kim

如果你的优化没有触及到瓶颈,你只是浪费你的时间,并没有解决实际问题。在你优化瓶颈之前,你不会得到任何重要的改进。如果你在不知道瓶颈是什么前就尝试优化,那么你最终只会在部分代码中玩耍。在测量和确定瓶颈之前优化代码被称为“过早优化”。人们常提及 Donald Knuth 说的话,但他声称这句话实际上是他从别人那里听来的:

过早优化是万恶之源 Premature optimization is the root of all evil

在谈到维护代码库时,来自 Donald Knuth 的更完整的引文是:

在 97% 的时间里,我们应该忘记微不足道的效率:过早的优化是万恶之源。然而在关 键的 3%,我们不应该错过优化的机会。 —— Donald Knuth

换句话说,他所说的是,在大多数时间你应该忘记对你的代码进行优化。它几乎总是足够好。在不是足够好的情况下,我们通常只需要触及 3% 的代码路径。比如因为你使用了 if 语句而不是函数,你的端点快了几纳秒,但这并不会使你赢得任何奖项。

过早的优化包括调用某些更快的函数,或者甚至使用特定的数据结构,因为它通常更快。计算机科学认为,如果一个方法或者算法与另一个具有相同的渐近增长(或称为 Big-O),那么它们是等价的,即使在实践中要慢两倍。计算机是如此之快,算法随着数据/使用增加而造成的计算增长远远超过实际速度本身。换句话说,如果你有两个 O(log n) 的函数,但是一个要慢两倍,这实际上并不重要。随着数据规模的增大,它们都以同样的速度“慢下来”。这就是过早优化是万恶之源的原因;它浪费了我们的时间,几乎从来没有真正有助于我们的性能改进。

就 Big-O 而言,你可以认为对你的程序而言,所有的语言都是 O(n),其中 n 是代码或者指令的行数。对于同样的指令,它们以同样的速率增长。对于渐进增长,一种语言的速度快慢并不重要,所有语言都是相同的。在这个逻辑下,你可以说,为你的应用程序选择一种语言仅仅是因为它的“快速”是过早优化的最终形式。你选择某些预期快速的东西,却没有测量,也不理解瓶颈将在哪里。

为您的应用选择语言只是因为它的“快速”,是过早优化的最终形式。

优化 Python

我最喜欢 Python 的一点是,它可以让你一次优化一点点代码。假设你有一个 Python 的方法,你发现它是你的瓶颈。你对它优化过几次,可能遵循这里那里的一些指导,现在,你很肯定 Python 本身就是你的瓶颈。Python 有调用 C 代码的能力,这意味着,你可以用 C 重写这个方法来减少性能问题。你可以一次重写一个这样的方法。这个过程允许你用任何可以编译为 C 兼容汇编程序的语言,编写良好优化后的瓶颈方法。这让你能够在大多数时间使用 Python 编写,只在必要的时候都才用较低级的语言来写代码。

有一种叫做 Cython 的编程语言,它是 Python 的超集。它几乎是 Python 和 C 的合并,是一种渐进类型的语言。任何 Python 代码都是有效的 Cython 代码,Cython 代码可以编译成 C 代码。使用 Cython,你可以编写一个模块或者一个方法,并逐渐进步到越来越多的 C 类型和性能。你可以将 C 类型和 Python 的鸭子类型混在一起。使用 Cython,你可以获得混合后的完美组合,只在瓶颈处进行优化,同时在其他所有地方不失去 Python 的美丽。

星战前夜的一幅截图:这是用 Python 编写的 space MMO 游戏。

当您最终遇到 Python 的性能问题阻碍时,你不需要把你的整个代码库用另一种不同的语言来编写。你只需要用 Cython 重写几个函数,几乎就能得到你所需要的性能。这就是星战前夜采取的策略。这是一个大型多玩家的电脑游戏,在整个架构中使用 Python 和 Cython。它们通过优化 C/Cython 中的瓶颈来实现游戏级别的性能。如果这个策略对他们有用,那么它应该对任何人都有帮助。或者,还有其他方法来优化你的 Python。例如,PyPy 是一个 Python 的 JIT 实现,它通过使用 PyPy 替掉 CPython(这是 Python 的默认实现),为长时间运行的应用程序提供重要的运行时改进(如 web 服务器)。

让我们回顾一下要点:

  • 优化你最贵的资源。那就是你,而不是计算机。
  • 选择一种语言/框架/架构来帮助你快速开发(比如 Python)。不要仅仅因为某些技术的快而选择它们。
  • 当你遇到性能问题时,请找到瓶颈所在。
  • 你的瓶颈很可能不是 CPU 或者 Python 本身。
  • 如果 Python 成为你的瓶颈(你已经优化过你的算法),那么可以转向热门的 Cython 或者 C。
  • 尽情享受可以快速做完事情的乐趣。

我希望你喜欢阅读这篇文章,就像我喜欢写这篇文章一样。如果你想说谢谢,请为我点下赞。另外,如果某个时候你想和我讨论 Python,你可以在 twitter 上艾特我(@nhumrich),或者你可以在 Python slack channel 找到我。


作者简介:

Nick Humrich -- 坚持采用持续交付的方法,并为之写了很多工具。同是还是一名 Python 黑客与技术狂热者,目前是一名 DevOps 工程师。

(题图:Pixabay,CC0)

via: https://medium.com/hacker-daily/yes-python-is-slow-and-i-dont-care-13763980b5a1

作者:Nick Humrich 译者:zhousiyu325 校对:jasminepeng

本文由 LCTT 原创编译,Linux中国 荣誉推出

最近几年中,面向数据的设计已经受到了很多的关注 —— 一种强调内存中数据布局的编程风格,包括如何访问以及将会引发多少的 cache 缺失。由于在内存读取操作中缺失所占的数量级要大于命中的数量级,所以缺失的数量通常是优化的关键标准。这不仅仅关乎那些对性能有要求的 code-data 结构设计的软件,由于缺乏对内存效益的重视而成为软件运行缓慢、膨胀的一个很大因素。

高效缓存数据结构的中心原则是将事情变得平滑和线性。比如,在大部分情况下,存储一个序列元素更倾向于使用普通数组而不是链表 —— 每一次通过指针来查找数据都会为 cache 缺失增加一份风险;而普通数组则可以预先获取,并使得内存系统以最大的效率运行

如果你知道一点内存层级如何运作的知识,下面的内容会是想当然的结果——但是有时候即便它们相当明显,测试一下任不失为一个好主意。几年前 Baptiste Wicht 测试过了 std::vector vs std::list vs std::deque,(后者通常使用分块数组来实现,比如:一个数组的数组)。结果大部分会和你预期的保持一致,但是会存在一些违反直觉的东西。作为实例:在序列链表的中间位置做插入或者移除操作被认为会比数组快,但如果该元素是一个 POD 类型,并且不大于 64 字节或者在 64 字节左右(在一个 cache 流水线内),通过对要操作的元素周围的数组元素进行移位操作要比从头遍历链表来的快。这是由于在遍历链表以及通过指针插入/删除元素的时候可能会导致不少的 cache 缺失,相对而言,数组移位则很少会发生。(对于更大的元素,非 POD 类型,或者你已经有了指向链表元素的指针,此时和预期的一样,链表胜出)

多亏了类似 Baptiste 这样的数据,我们知道了内存布局如何影响序列容器。但是关联容器,比如 hash 表会怎么样呢?已经有了些权威推荐:Chandler Carruth 推荐的带局部探测的开放寻址(此时,我们没必要追踪指针),以及Mike Acton 推荐的在内存中将 value 和 key 隔离(这种情况下,我们可以在每一个 cache 流水线中得到更多的 key), 这可以在我们必须查找多个 key 时提高局部性能。这些想法很有意义,但再一次的说明:测试永远是好习惯,但由于我找不到任何数据,所以只好自己收集了。

测试

我测试了四个不同的 quick-and-dirty 哈希表实现,另外还包括 std::unordered_map 。这五个哈希表都使用了同一个哈希函数 —— Bob Jenkins 的 SpookyHash(64 位哈希值)。(由于哈希函数在这里不是重点,所以我没有测试不同的哈希函数;我同样也没有检测我的分析中的总内存消耗。)实现会通过简短的代码在测试结果表中标注出来。

  • UMstd::unordered_map 。在 VS2012 和 libstdc++-v3 (libstdc++-v3: gcc 和 clang 都会用到这东西)中,UM 是以链表的形式实现,所有的元素都在链表中,bucket 数组中存储了链表的迭代器。VS2012 中,则是一个双链表,每一个 bucket 存储了起始迭代器和结束迭代器;libstdc++ 中,是一个单链表,每一个 bucket 只存储了一个起始迭代器。这两种情况里,链表节点是独立申请和释放的。最大负载因子是 1 。
  • Ch:分离的、链状 bucket 指向一个元素节点的单链表。为了避免分开申请每一个节点,元素节点存储在普通数组池中。未使用的节点保存在一个空闲链表中。最大负载因子是 1。
  • OL:开地址线性探测 —— 每一个 bucket 存储一个 62 bit 的 hash 值,一个 2 bit 的状态值(包括 empty,filled,removed 三个状态),key 和 value 。最大负载因子是 2/3。
  • DO1:“data-oriented 1” —— 和 OL 相似,但是哈希值、状态值和 key、values 分离在两个隔离的平滑数组中。
  • DO2:“data-oriented 2” —— 与 OL 类似,但是哈希/状态,keys 和 values 被分离在 3 个相隔离的平滑数组中。

在我的所有实现中,包括 VS2012 的 UM 实现,默认使用尺寸为 2 的 n 次方。如果超出了最大负载因子,则扩展两倍。在 libstdc++ 中,UM 默认尺寸是一个素数。如果超出了最大负载因子,则扩展为下一个素数大小。但是我不认为这些细节对性能很重要。素数是一种对低 bit 位上没有足够熵的低劣 hash 函数的挽救手段,但是我们正在用的是一个很好的 hash 函数。

OL,DO1 和 DO2 的实现将共同的被称为 OA(open addressing)——稍后我们将发现它们在性能特性上非常相似。在每一个实现中,单元数从 100 K 到 1 M,有效负载(比如:总的 key + value 大小)从 8 到 4 k 字节我为几个不同的操作记了时间。 keys 和 values 永远是 POD 类型,keys 永远是 8 个字节(除了 8 字节的有效负载,此时 key 和 value 都是 4 字节)因为我的目的是为了测试内存影响而不是哈希函数性能,所以我将 key 放在连续的尺寸空间中。每一个测试都会重复 5 遍,然后记录最小的耗时。

测试的操作在这里:

  • Fill:将一个随机的 key 序列插入到表中(key 在序列中是唯一的)。
  • Presized fill:和 Fill 相似,但是在插入之间我们先为所有的 key 保留足够的内存空间,以防止在 fill 过程中 rehash 或者重申请。
  • Lookup:执行 100 k 次随机 key 查找,所有的 key 都在 table 中。
  • Failed lookup: 执行 100 k 次随机 key 查找,所有的 key 都不在 table 中。
  • Remove:从 table 中移除随机选择的半数元素。
  • Destruct:销毁 table 并释放内存。

你可以在这里下载我的测试代码。这些代码只能在 64 机器上编译(包括Windows和Linux)。在 main() 函数顶部附近有一些开关,你可把它们打开或者关掉——如果全开,可能会需要一两个小时才能结束运行。我收集的结果也放在了那个打包文件里的 Excel 表中。(注意: Windows 和 Linux 在不同的 CPU 上跑的,所以时间不具备可比较性)代码也跑了一些单元测试,用来验证所有的 hash 表实现都能运行正确。

我还顺带尝试了附加的两个实现:Ch 中第一个节点存放在 bucket 中而不是 pool 里,二次探测的开放寻址。 这两个都不足以好到可以放在最终的数据里,但是它们的代码仍放在了打包文件里面。

结果

这里有成吨的数据!! 这一节我将详细的讨论一下结果,但是如果你对此不感兴趣,可以直接跳到下一节的总结。

Windows

这是所有的测试的图表结果,使用 Visual Studio 2012 编译,运行于 Windows 8.1 和 Core i7-4710HQ 机器上。

 title=

从左至右是不同的有效负载大小,从上往下是不同的操作(注意:不是所有的Y轴都是相同的比例!)我将为每一个操作总结一下主要趋向。

Fill

在我的 hash 表中,Ch 稍比任何的 OA 变种要好。随着哈希表大小和有效负载的加大,差距也随之变大。我猜测这是由于 Ch 只需要从一个空闲链表中拉取一个元素,然后把它放在 bucket 前面,而 OA 不得不搜索一部分 bucket 来找到一个空位置。所有的 OA 变种的性能表现基本都很相似,当然 DO1 稍微有点优势。

在小负载的情况,UM 几乎是所有 hash 表中表现最差的 —— 因为 UM 为每一次的插入申请(内存)付出了沉重的代价。但是在 128 字节的时候,这些 hash 表基本相当,大负载的时候 UM 还赢了点。因为,我所有的实现都需要重新调整元素池的大小,并需要移动大量的元素到新池里面,这一点我几乎无能为力;而 UM 一旦为元素申请了内存后便不需要移动了。注意大负载中图表上夸张的跳步!这更确认了重新调整大小带来的问题。相反,UM 只是线性上升 —— 只需要重新调整 bucket 数组大小。由于没有太多隆起的地方,所以相对有效率。

Presized fill

大致和 Fill 相似,但是图示结果更加的线性光滑,没有太大的跳步(因为不需要 rehash ),所有的实现差距在这一测试中要缩小了些。大负载时 UM 依然稍快于 Ch,问题还是在于重新调整大小上。Ch 仍是稳步少快于 OA 变种,但是 DO1 比其它的 OA 稍有优势。

Lookup

所有的实现都相当的集中。除了最小负载时,DO1 和 OL 稍快,其余情况下 UM 和 DO2 都跑在了前面。(LCTT 译注: 你确定?)真的,我无法描述 UM 在这一步做的多么好。尽管需要遍历链表,但是 UM 还是坚守了面向数据的本性。

顺带一提,查找时间和 hash 表的大小有着很弱的关联,这真的很有意思。 哈希表查找时间期望上是一个常量时间,所以在的渐进视图中,性能不应该依赖于表的大小。但是那是在忽视了 cache 影响的情况下!作为具体的例子,当我们在具有 10 k 条目的表中做 100 k 次查找时,速度会便变快,因为在第一次 10 k - 20 k 次查找后,大部分的表会处在 L3 中。

Failed lookup

相对于成功查找,这里就有点更分散一些。DO1 和 DO2 跑在了前面,但 UM 并没有落下,OL 则是捉襟见肘啊。我猜测,这可能是因为 OL 整体上具有更长的搜索路径,尤其是在失败查询时;内存中,hash 值在 key 和 value 之飘来荡去的找不着出路,我也很受伤啊。DO1 和 DO2 具有相同的搜索长度,但是它们将所有的 hash 值打包在内存中,这使得问题有所缓解。

Remove

DO2 很显然是赢家,但 DO1 也未落下。Ch 则落后,UM 则是差的不是一丁半点(主要是因为每次移除都要释放内存);差距随着负载的增加而拉大。移除操作是唯一不需要接触数据的操作,只需要 hash 值和 key 的帮助,这也是为什么 DO1 和 DO2 在移除操作中的表现大相径庭,而其它测试中却保持一致。(如果你的值不是 POD 类型的,并需要析构,这种差异应该是会消失的。)

Destruct

Ch 除了最小负载,其它的情况都是最快的(最小负载时,约等于 OA 变种)。所有的 OA 变种基本都是相等的。注意,在我的 hash 表中所做的所有析构操作都是释放少量的内存 buffer 。但是 在Windows中,释放内存的消耗和大小成比例关系。(而且,这是一个很显著的开支 —— 申请 ~1 GB 的内存需要 ~100 ms 的时间去释放!)

UM 在析构时是最慢的一个(小负载时,慢的程度可以用数量级来衡量),大负载时依旧是稍慢些。对于 UM 来讲,释放每一个元素而不是释放一组数组真的是一个硬伤。

Linux

我还在装有 Linux Mint 17.1 的 Core i5-4570S 机器上使用 gcc 4.8 和 clang 3.5 来运行了测试。gcc 和 clang 的结果很相像,因此我只展示了 gcc 的;完整的结果集合包含在了代码下载打包文件中,链接在上面。

 title=

大部分结果和 Windows 很相似,因此我只高亮了一些有趣的不同点。

Lookup

这里 DO1 跑在前头,而在 Windows 中 DO2 更快些。(LCTT 译注: 这里原文写错了吧?)同样,UM 和 Ch 落后于其它所有的实现——过多的指针追踪,然而 OA 只需要在内存中线性的移动即可。至于 Windows 和 Linux 结果为何不同,则不是很清楚。UM 同样比 Ch 慢了不少,特别是大负载时,这很奇怪;我期望的是它们可以基本相同。

Failed lookup

UM 再一次落后于其它实现,甚至比 OL 还要慢。我再一次无法理解为何 UM 比 Ch 慢这么多,Linux 和 Windows 的结果为何有着如此大的差距。

Destruct

在我的实现中,小负载的时候,析构的消耗太少了,以至于无法测量;在大负载中,线性增加的比例和创建的虚拟内存页数量相关,而不是申请到的数量?同样,要比 Windows 中的析构快上几个数量级。但是并不是所有的都和 hash 表有关;我们在这里可以看出不同系统和运行时内存系统的表现。貌似,Linux 释放大内存块是要比 Windows 快上不少(或者 Linux 很好的隐藏了开支,或许将释放工作推迟到了进程退出,又或者将工作推给了其它线程或者进程)。

UM 由于要释放每一个元素,所以在所有的负载中都比其它慢上几个数量级。事实上,我将图片做了剪裁,因为 UM 太慢了,以至于破坏了 Y 轴的比例。

总结

好,当我们凝视各种情况下的数据和矛盾的结果时,我们可以得出什么结果呢?我想直接了当的告诉你这些 hash 表变种中有一个打败了其它所有的 hash 表,但是这显然不那么简单。不过我们仍然可以学到一些东西。

首先,在大多数情况下我们“很容易”做的比 std::unordered_map 还要好。我为这些测试所写的所有实现(它们并不复杂;我只花了一两个小时就写完了)要么是符合 unordered_map 要么是在其基础上做的提高,除了大负载(超过128字节)中的插入性能, unordered_map 为每一个节点独立申请存储占了优势。(尽管我没有测试,我同样期望 unordered_map 能在非 POD 类型的负载上取得胜利。)具有指导意义的是,如果你非常关心性能,不要假设你的标准库中的数据结构是高度优化的。它们可能只是在 C++ 标准的一致性上做了优化,但不是性能。:P

其次,如果不管在小负载还是超负载中,若都只用 DO1 (开放寻址,线性探测,hashes/states 和 key/vaules分别处在隔离的普通数组中),那可能不会有啥好表现。这不是最快的插入,但也不坏(还比 unordered_map 快),并且在查找,移除,析构中也很快。你所知道的 —— “面向数据设计”完成了!

注意,我的为这些哈希表做的测试代码远未能用于生产环境——它们只支持 POD 类型,没有拷贝构造函数以及类似的东西,也未检测重复的 key,等等。我将可能尽快的构建一些实际的 hash 表,用于我的实用库中。为了覆盖基础部分,我想我将有两个变种:一个基于 DO1,用于小的,移动时不需要太大开支的负载;另一个用于链接并且避免重新申请和移动元素(就像 unordered_map ),用于大负载或者移动起来需要大开支的负载情况。这应该会给我带来最好的两个世界。

与此同时,我希望你们会有所启迪。最后记住,如果 Chandler Carruth 和 Mike Acton 在数据结构上给你提出些建议,你一定要听。


作者简介:

我是一名图形程序员,目前在西雅图做自由职业者。之前我在 NVIDIA 的 DevTech 软件团队中工作,并在美少女特工队工作室中为 PS3 和 PS4 的 Infamous 系列游戏开发渲染技术。

自 2002 年起,我对图形非常感兴趣,并且已经完成了一系列的工作,包括:雾、大气雾霾、体积照明、水、视觉效果、粒子系统、皮肤和头发阴影、后处理、镜面模型、线性空间渲染、和 GPU 性能测量和优化。

你可以在我的博客了解更多和我有关的事,除了图形,我还对理论物理和程序设计感兴趣。

你可以在 [email protected] 或者在 Twitter(@Reedbeta)/Google+ 上关注我。我也会经常在 StackExchange 上回答计算机图形的问题。


via: http://reedbeta.com/blog/data-oriented-hash-table/

作者:Nathan Reed 译者:sanfusu 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

本文作者 Brendan Gregg 目前是 Netflix 的高级性能架构师 ,他在那里做大规模计算机性能设计、分析和调优。他是《Systems Performance》等技术书的作者,因在系统管理员方面的成绩,获得过 2013年 USENIX LISA 大奖。他之前是 SUN 公司是性能领头人和内核工程师,研究存储和网络性能。他也发明和开发过一大波性能分析工具,很多已集成到操作系统中了 。

随着 BPF 追踪系统(基于时间采样)最后一个主要功能被合并至 Linux 4.9-rc1 版本的内核中,现在 Linux 内核拥有类似 DTrace 的原生追踪功能。DTrace 是 Solaris 系统中的高级追踪器。对于长期使用 DTrace 的用户和专家,这将是一个振奋人心的里程碑!现在在 Linux 系统上,你可以在生产环境中使用安全的、低负载的定制追踪系统,通过执行时间的柱状图和频率统计等信息,分析应用的性能以及内核。

用于 Linux 的追踪项目有很多,但是这个最终被合并进 Linux 内核的技术从一开始就根本不是一个追踪项目:它是最开始是用于 伯克利包过滤器 Berkeley Packet Filter (BPF)的增强功能。这些补丁允许 BPF 重定向数据包,从而创建软件定义网络(SDN)。久而久之,对事件追踪的支持就被添加进来了,使得程序追踪可用于 Linux 系统。

尽管目前 BPF 没有像 DTrace 一样的高级语言,但它所提供的前端已经足够让我创建很多 BPF 工具了,其中有些是基于我以前的 DTraceToolkit。这个帖子将告诉你怎么去用这些 BPF 提供的前端工具,以及畅谈这项技术将会何去何从。

示例

我已经将基于 BPF 的追踪工具添加到了开源的 bcc 项目里(感谢 PLUMgrid 公司的 Brenden Blanco 带领 bcc 项目的发展)。详见 bcc 安装 手册。它会在 /usr/share/bcc/tools 目录下添加一系列工具,包括接下来的那些工具。

捕获新进程:

# execsnoop
PCOMM            PID    RET ARGS
bash             15887    0 /usr/bin/man ls
preconv          15894    0 /usr/bin/preconv -e UTF-8
man              15896    0 /usr/bin/tbl
man              15897    0 /usr/bin/nroff -mandoc -rLL=169n -rLT=169n -Tutf8
man              15898    0 /usr/bin/pager -s
nroff            15900    0 /usr/bin/locale charmap
nroff            15901    0 /usr/bin/groff -mtty-char -Tutf8 -mandoc -rLL=169n -rLT=169n
groff            15902    0 /usr/bin/troff -mtty-char -mandoc -rLL=169n -rLT=169n -Tutf8
groff            15903    0 /usr/bin/grotty

硬盘 I/O 延迟的柱状图:

# biolatency -m
Tracing block device I/O... Hit Ctrl-C to end.
^C
     msecs           : count     distribution
       0 -> 1        : 96       |************************************  |
       2 -> 3        : 25       |*********                             |
       4 -> 7        : 29       |***********                           |
       8 -> 15       : 62       |***********************               |
      16 -> 31       : 100      |**************************************|
      32 -> 63       : 62       |***********************               |
      64 -> 127      : 18       |******                                |

追踪慢于 5 毫秒的 ext4 常见操作:

# ext4slower 5
Tracing ext4 operations slower than 5 ms
TIME     COMM           PID    T BYTES   OFF_KB   LAT(ms) FILENAME
21:49:45 supervise      3570   W 18      0           5.48 status.new
21:49:48 supervise      12770  R 128     0           7.55 run
21:49:48 run            12770  R 497     0          16.46 nsswitch.conf
21:49:48 run            12770  R 1680    0          17.42 netflix_environment.sh
21:49:48 run            12770  R 1079    0           9.53 service_functions.sh
21:49:48 run            12772  R 128     0          17.74 svstat
21:49:48 svstat         12772  R 18      0           8.67 status
21:49:48 run            12774  R 128     0          15.76 stat
21:49:48 run            12777  R 128     0           7.89 grep
21:49:48 run            12776  R 128     0           8.25 ps
21:49:48 run            12780  R 128     0          11.07 xargs
21:49:48 ps             12776  R 832     0          12.02 libprocps.so.4.0.0
21:49:48 run            12779  R 128     0          13.21 cut
[...]

追踪新建的 TCP 活跃连接(connect()):

# tcpconnect
PID    COMM         IP SADDR            DADDR            DPORT
1479   telnet       4  127.0.0.1        127.0.0.1        23
1469   curl         4  10.201.219.236   54.245.105.25    80
1469   curl         4  10.201.219.236   54.67.101.145    80
1991   telnet       6  ::1              ::1              23
2015   ssh          6  fe80::2000:bff:fe82:3ac fe80::2000:bff:fe82:3ac 22

通过跟踪 getaddrinfo()/gethostbyname() 库的调用来追踪 DNS 延迟:

# gethostlatency
TIME      PID    COMM          LATms HOST
06:10:24  28011  wget          90.00 www.iovisor.org
06:10:28  28127  wget           0.00 www.iovisor.org
06:10:41  28404  wget           9.00 www.netflix.com
06:10:48  28544  curl          35.00 www.netflix.com.au
06:11:10  29054  curl          31.00 www.plumgrid.com
06:11:16  29195  curl           3.00 www.facebook.com
06:11:25  29404  curl          72.00 foo
06:11:28  29475  curl           1.00 foo

按类别划分 VFS 操作的时间间隔统计:

# vfsstat
TIME         READ/s  WRITE/s CREATE/s   OPEN/s  FSYNC/s
18:35:32:       231       12        4       98        0
18:35:33:       274       13        4      106        0
18:35:34:       586       86        4      251        0
18:35:35:       241       15        4       99        0

对一个给定的 PID,通过内核和用户堆栈轨迹来追踪 CPU 处理之外的时间(由内核进行统计):

# offcputime -d -p 24347
Tracing off-CPU time (us) of PID 24347 by user + kernel stack... Hit Ctrl-C to end.
^C
[...]
    ffffffff810a9581 finish_task_switch
    ffffffff8185d385 schedule
    ffffffff81085672 do_wait
    ffffffff8108687b sys_wait4
    ffffffff81861bf6 entry_SYSCALL_64_fastpath
    --
    00007f6733a6b64a waitpid
    -                bash (24347)
        4952

    ffffffff810a9581 finish_task_switch
    ffffffff8185d385 schedule
    ffffffff81860c48 schedule_timeout
    ffffffff810c5672 wait_woken
    ffffffff8150715a n_tty_read
    ffffffff815010f2 tty_read
    ffffffff8122cd67 __vfs_read
    ffffffff8122df65 vfs_read
    ffffffff8122f465 sys_read
    ffffffff81861bf6 entry_SYSCALL_64_fastpath
    --
    00007f6733a969b0 read
    -                bash (24347)
        1450908

追踪 MySQL 查询延迟(通过 USDT 探针):

# mysqld_qslower `pgrep -n mysqld`
Tracing MySQL server queries for PID 14371 slower than 1 ms...
TIME(s)        PID          MS QUERY
0.000000       18608   130.751 SELECT * FROM words WHERE word REGEXP '^bre.*n$'
2.921535       18608   130.590 SELECT * FROM words WHERE word REGEXP '^alex.*$'
4.603549       18608    24.164 SELECT COUNT(*) FROM words
9.733847       18608   130.936 SELECT count(*) AS count FROM words WHERE word REGEXP '^bre.*n$'
17.864776      18608   130.298 SELECT * FROM words WHERE word REGEXP '^bre.*n$' ORDER BY word

监测 pam 库并使用多种追踪工具观察登录请求:

# trace 'pam:pam_start "%s: %s", arg1, arg2'
TIME     PID    COMM         FUNC             -
17:49:45 5558   sshd         pam_start        sshd: root
17:49:47 5662   sudo         pam_start        sudo: root
17:49:49 5727   login        pam_start        login: bgregg

bcc 项目里的很多工具都有帮助信息(-h 选项),并且都应该包含有示例的 man 页面和文本文件。

必要性

2014 年,Linux 追踪程序就有一些内核相关的特性(来自 ftracepref_events),但是我们仍然要转储并报告进程数据,这种几十年前的老技术有很多的限制。你不能频繁地访问进程名、函数名、堆栈轨迹或内核中的任意的其它数据。你不能在将变量保存到一个监测事件里,又在另一个事件里访问它们,这意味着你不能在你需要的地方计算延迟(或者说时间增量)。你也不能创建一个内核内部的延迟柱状图,也不能追踪 USDT 探针,甚至不能写个自定义的程序。DTrace 可以做到所有这些,但仅限于 Solaris 或 BSD 系统。在 Linux 系统中,有些不在主线内核的追踪器,比如 SystemTap 就可以满足你的这些需求,但它也有自身的不足。(理论上说,你可以写一个基于探针的内核模块来满足需求-但实际上没人这么做。)

2014 年我加入了 Netflix cloud performance 团队。做了这么久的 DTrace 方面的专家,转到 Linux 对我来说简直不可思议。但我确实这么做了,而且遇到了巨大的挑战:在应用快速变化、采用微服务架构和分布式系统的情况下,调优 Netflix cloud。有时要用到系统追踪,而我之前是用的 DTrace。在 Linux 系统上可没有 DTrace,我就开始用 Linux 内核内建的 ftraceperf_events 工具,构建了一个追踪工具(perf-tools)。这些工具很有用,但有些工作还是没法完成,尤其是延迟柱状图以及堆栈踪迹计数。我们需要的是内核追踪的可程序化。

发生了什么?

BPF 将程序化的功能添加到现有的内核追踪工具中(tracepointskprobesuprobes)。在 Linux 4.x 系列的内核里,这些功能大大加强了。

时间采样是最主要的部分,它被 Linux 4.9-rc1 所采用(patchset)。十分感谢 Alexei Starovoitov(在 Facebook 致力于 BPF 的开发),他是这些 BPF 增强功能的主要开发者。

Linux 内核现在内建有以下这些特性(自 2.6 版本到 4.9 版本之间增加):

  • 内核级的动态追踪(BPF 对 kprobes 的支持)
  • 用户级的动态追踪(BPF 对 uprobes 的支持)
  • 内核级的静态追踪(BPF 对 tracepoints 的支持)
  • 时间采样事件(BPF 的 pref_event_open
  • PMC 事件(BPF 的 pref_event_open
  • 过滤器(通过 BPF 程序)
  • 调试输出(bpf_trace_printk()
  • 按事件输出(bpf_perf_event_output()
  • 基础变量(全局的和每个线程的变量,基于 BPF 映射)
  • 关联数组(通过 BPF 映射)
  • 频率计数(基于 BPF 映射)
  • 柱状图(2 的冥次方、线性及自定义,基于 BPF 映射)
  • 时间戳和时间增量(bpf_ktime_get_ns(),和 BPF 程序)
  • 内核态的堆栈轨迹(BPF 栈映射)
  • 用户态的堆栈轨迹 (BPF 栈映射)
  • 重写 ring 缓存(pref_event_attr.write_backward

我们采用的前端是 bcc,它同时提供 Python 和 lua 接口。bcc 添加了:

  • 用户级静态追踪(基于 uprobes 的 USDT 探针)
  • 调试输出(Python 中调用 BPF.trace_pipe()BPF.trace_fields() 函数 )
  • 按事件输出(BPF_PERF_OUTPUT 宏和 BPF.open_perf_buffer()
  • 间隔输出(BPF.get_table()table.clear()
  • 打印柱状图(table.print_log2_hist()
  • 内核级的 C 结构体导航(bcc 重写器映射到 bpf_probe_read() 函数)
  • 内核级的符号解析(ksym()ksymaddr()
  • 用户级的符号解析(usymaddr()
  • BPF 跟踪点支持(通过 TRACEPOINT_PROBE
  • BPF 堆栈轨迹支持(包括针对堆栈框架的 walk 方法)
  • 其它各种辅助宏和方法
  • 例子(位于 /examples 目录)
  • 工具(位于 /tools 目录)
  • 教程(/docs/tutorial*.md
  • 参考手册(/docs/reference_guide.md

直到最新也是最主要的特性被整合进来,我才开始写这篇文章,现在它在 4.9-rc1 内核中。我们还需要去完成一些次要的东西,还有另外一些事情要做,但是现在我们所拥有的已经值得欢呼了。现在 Linux 拥有了内建的高级追踪能力。

安全性

设计 BPF 及其增强功能时就考虑到生产环境级安全,它被用在大范围的生产环境里。不过你想的话,你还是可以找到一个挂起内核的方法。这种情况是偶然的,而不是必然,类似的漏洞会被快速修复,尤其是当 BPF 合并入了 Linux。因为 Linux 可是公众的焦点。

在开发过程中我们碰到了一些非 BPF 的漏洞,它们需要被修复:rcu 不可重入,这可能导致内核由于 funccount 挂起,在 4.6 内核版本中这个漏洞被 “bpf: map pre-alloc” 补丁集所修复,旧版本内核的漏洞 bcc 有个临时处理方案。还有一个是 uprobe 的内存计算问题,这导致 uprobe 分配内存失败,在 4.8 内核版本这个漏洞由 “uprobes: Fix the memcg accounting” 补丁所修复,并且该补丁还将被移植到之前版本的内核中(例如,它现在被移植到了 4.4.27 和 4.4.0-45.66 版本中)。

为什么 Linux 追踪用了这么久才加进来?

首要任务被分到了若干追踪器中间:这些不是某个追踪器单个的事情。想要了解更多关于这个或其它方面的问题,可以看一看我在 2014 年 tracing summit 上的讲话。我忽视了部分方案的反面影响:有些公司发现其它追踪器(SystemTap 和 LTTng)能满足他们的需求,尽管他们乐于听到 BPF 的开发进程,但考虑到他们现有的解决方案,帮助 BPF 的开发就不那么重要了。

BPF 仅在近两年里在追踪领域得到加强。这一过程原本可以更快的,但早期缺少全职从事于 BPF 追踪的工程师。Alexei Starovoitov (BPF 领导者),Brenden Blanco (bcc 领导者),我还有其它一些开发者,都有其它的事情要做。我在 Netflix 公司花了大量时间(志愿地),大概有 7% 的时间是花在 BPF 和 bcc 上。某种程度上这不是我的首要任务,因为我还有自己的工作(包括我的 perf-tools,一个可以工作在旧版本内核上的程序)。

现在BPF 追踪器已经推出了,已经有科技公司开始寻找会 BPF 的人了。但我还是推荐 Netflix 公司。(如果你为了 BPF 而要聘请我,那我还是十分乐于待在 Netflix 公司的!)

使用简单

DTrace 和 bcc/BPF 现在的最大区别就是哪个更好使用。这取决于你要用 BPF 追踪做什么了。如果你要

  • 使用 BPF 工具/度量:应该是没什么区别的。工具的表现都差不多,图形用户界面都能取得类似度量指标。大部分用户通过这种方式使用 BPF。
  • 开发工具/度量:bcc 的开发可难多了。DTrace 有一套自己的简单语言,D 语音,和 awk 语言相似,而 bcc 使用已有的语言(C 语言,Python 和 lua)及其类库。一个用 C 和 Python 写的 bcc 工具与仅仅用 D 语言写出来的工具相比,可能要多十多倍行数的代码,或者更多。但是很多 DTrace 工具用 shell 封装来提供参数和差错检查,会让代码变得十分臃肿。编程的难处是不同的:重写 bcc 更需要巧妙性,这导致某些脚本更加难开发。(尤其是 bpf_probe_read() 这类的函数,需要了解更多 BPF 的内涵知识)。当计划改进 bcc 时,这一情形将得到改善。
  • 运行常见的命令:十分相近。通过 dtrace 命令,DTrace 能做很多事,但 bcc 有各种工具,traceargdistfunccountfunclatency 等等。
  • 编写自定义的特殊命令:使用 DTrace 的话,这就没有必要了。允许定制消息快速传递和系统快速响应,DTrace 的高级分析很快。而 bcc 现在受限于它的多种工具以及它们的适用范围。

简单来说,如果你只使用 BPF 工具的话,就不必关注这些差异了。如果你经验丰富,是个开发者(像我一样),目前 bcc 的使用更难一些。

举一个 bcc 的 Python 前端的例子,下面是追踪硬盘 I/O 并打印出 I/O 大小的柱状图代码:

from bcc import BPF
from time import sleep

# load BPF program
b = BPF(text="""
#include <uapi/linux/ptrace.h>
#include <linux/blkdev.h>

BPF_HISTOGRAM(dist);

int kprobe__blk_account_io_completion(struct pt_regs *ctx, struct request *req)
{
    dist.increment(bpf_log2l(req->__data_len / 1024));
    return 0;
}
""")

# header
print("Tracing... Hit Ctrl-C to end.")

# trace until Ctrl-C
try:
    sleep(99999999)
except KeyboardInterrupt:
    print

# output
b["dist"].print_log2_hist("kbytes")

注意 Python 代码中嵌入的 C 语句(text=)。

这就完成了任务,但仍有改进的空间。好在我们有时间去做:人们使用 Linux 4.9 并能用上 BPF 还得好几个月呢,所以我们有时间来制造工具和前端。

高级语言

前端越简单,比如高级语言,所改进的可能就越不如你所期望的。绝大多数人使用封装好的工具(和图形界面),仅有少部分人能写出这些工具。但我不反对使用高级语言,比如 SystemTap,毕竟已经开发出来了。

#!/usr/bin/stap
/*
 * opensnoop.stp    Trace file open()s.  Basic version of opensnoop.
 */

probe begin
{
    printf("\n%6s %6s %16s %s\n", "UID", "PID", "COMM", "PATH");
}

probe syscall.open
{
    printf("%6d %6d %16s %s\n", uid(), pid(), execname(), filename);
}

如果拥有整合了语言和脚本的 SystemTap 前端与高性能的内置在内核中的 BPF 后端,会不会令人满意呢?RedHat 公司的 Richard Henderson 已经在进行相关工作了,并且发布了 初代版本

这是 ply,一个完全新颖的 BPF 高级语言:

#!/usr/bin/env ply

kprobe:SyS_*
{
    $syscalls[func].count()
}

这也是一份承诺。

尽管如此,我认为工具开发者的实际难题不是使用什么语言:而是要了解要用这些强大的工具做什么?

如何帮助我们

  • 推广:BPF 追踪器目前还没有什么市场方面的进展。尽管有公司了解并在使用它(Facebook、Netflix、Github 和其它公司),但要广为人知尚需时日。你可以分享关于 BPF 的文章和资源给业内的其它公司来帮助我们。
  • 教育:你可以撰写文章,发表演讲,甚至参与 bcc 文档的编写。分享 BPF 如何解决实际问题以及为公司带来收益的实例。
  • 解决 bcc 的问题:参考 bcc issue list,这包含了错误和需要的特性。
  • 提交错误:使用 bcc/BPF,提交你发现的错误。
  • 创造工具:有很多可视化的工具需要开发,但请不要太草率,因为大家会先花几个小时学习使用你做的工具,所以请尽量把工具做的直观好用(参考我的文档)。就像 Mike Muuss 提及到他自己的 ping 程序:“要是我早知道这是我一生中最出名的成就,我就多开发一两天,添加更多选项。”
  • 高级语言:如果现有的 bcc 前端语言让你很困扰,或许你能弄门更好的语言。要是你想将这门语言内建到 bcc 里面,你需要使用 libbcc。或者你可以帮助 SystemTap BPF 或 ply 的工作。
  • 整合图形界面:除了 bcc 可以使用的 CLI 命令行工具,怎么让这些信息可视呢?延迟热点图,火焰图等等。

其它追踪器

那么 SystemTap、ktap、sysdig、LTTng 等追踪器怎么样呢?它们有个共同点,要么使用了 BPF,要么在自己的领域做得更好。会有单独的文章介绍它们自己。

至于 DTrace ?我们公司目前还在基于 FreeBSD 系统的 CDN 中使用它。

更多 bcc/BPF 的信息

我已经写了一篇《bcc/BPF 工具最终用户教程》,一篇《bcc Python 开发者教程》,一篇《bcc/BPF 参考手册》,并提供了一些有用的工具,每一个工具都有一个 example.txt 文件和 man page。我之前写过的关于 bcc 和 BPF 的文章有:

我在 Facebook 的 Performance@Scale Linux BPF Superpowers 大会上发表过一次演讲。十二月份,我将在 Boston 发表关于 BPF/bcc 在 USENIX LISA 方面的演讲和教程。

致谢

  • Van Jacobson 和 Steve McCanne,他们创建了最初用作过滤器的 BPF 。
  • Barton P. Miller,Jeffrey K. Hollingsworth,and Jon Cargille,发明了动态追踪,并发表论文《Dynamic Program Instrumentation for Scalable Performance Tools》,可扩展高性能计算协议 (SHPCC),于田纳西州诺克斯维尔市,1994 年 5 月发表。
  • kerninst (ParaDyn, UW-Madison),展示了动态跟踪的价值的早期动态跟踪工具(上世纪 90 年代后期)
  • Mathieu Desnoyers (在 LTTng),内核的主要开发者,主导 tracepoints 项目。
  • IBM 开发的作为 DProbes 一部分的 kprobes,DProbes 在 2000 年时曾与 LTT 一起提供 Linux 动态追踪,但没有整合到一起。
  • Bryan Cantrill, Mike Shapiro, and Adam Leventhal (Sun Microsystems),DTrace 的核心开发者,DTrace 是一款很棒的动态追踪工具,安全而且简单(2004 年)。对于动态追踪技术,DTrace 是科技的重要转折点:它很安全,默认安装在 Solaris 以及其它以可靠性著称的系统里。
  • 来自 Sun Microsystems 的各部门的许多员工,促进了 DTrace,为我们带来了高级系统追踪的意识。
  • Roland McGrath (在 Red Hat),utrace 项目的主要开发者,utrace 变成了后来的 uprobes。
  • Alexei Starovoitov (PLUMgrid, 后来是 Facebook),加强版 BPF(可编程内核部件)的主要开发者。
  • 那些帮助反馈、提交代码、测试以及针对增强版 BPF 补丁(请在 lkml 搜索 BPF)的 Linux 内核工程师: Wang Nan、 Daniel Borkmann、 David S. Miller、 Peter Zijlstra 以及其它很多人。
  • Brenden Blanco (PLUMgrid),bcc 的主要开发者。
  • Sasha Goldshtein (Sela) 开发了 bcc 中的跟踪点支持,和功能最强大的 bcc 工具 trace 及 argdist,帮助 USDT 项目的开发。
  • Vicent Martí 和其它 Github 上的工程师,为 bcc 编写了基于 lua 的前端,帮助 USDT 部分项目的开发。
  • Allan McAleavy、 Mark Drayton,和其他的改进 bcc 的贡献者。

感觉 Netflix 提供的环境和支持,让我能够编写 BPF 和 bcc 跟踪器并完成它们。我已经编写了多年的追踪工具(使用 TNF/prex、DTrace、SystemTap、ktap、ftrace、perf,现在是 bcc/BPF),并写书、博客以及评论,

最后,感谢 Deirdré 编辑了另外一篇文章。

总结

Linux 没有 DTrace(语言),但它现在有了,或者说拥有了 DTraceTookit(工具)。

通过增强内置的 BPF 引擎,Linux 4.9 内核拥有了用来支持现代化追踪的最后一项能力。内核支持这一最难的部分已经做完了。今后的任务包括更多的命令行执行工具,以及高级语言和图形用户界面。

对于性能分析产品的客户,这也是一件好事:你能查看延迟柱状图和热点图,CPU 处理和 CPU 之外的火焰图,拥有更好的时延断点和更低耗的工具。在用户空间按包跟踪和处理是没有效率的方式。

那么你什么时候会升级到 Linux 4.9 呢?一旦官方发布,新的性能测试工具就来了:apt-get install bcc-tools

开始享受它吧!

Brendan


via: http://www.brendangregg.com/blog/2016-10-27/dtrace-for-linux-2016.html

作者:Brendan Gregg 译者:GitFuture 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

巨头之间的终极对决:崛起的新星 Node.js 能否战胜巨人 Apache 和 Nginx?

我和你一样,都阅读过大量散布在互联网各处的意见或事实,其中有一些我认为是可靠的,而其它的可能是谣传,让人难以置信。

我读过的许多信息是相当矛盾的,有人深信 StackOverflow(比如这个另一个),而其他人展示了一个清晰的令人惊讶的结果,这在推动我自己去做测试来验证结论的过程中扮演了重要的角色。

起初,我做了一些思想准备,我认为我可以避免自己进行实际测试来校验结论的麻烦——在我知道这一切之前我一直这样认为。

尽管如此,回顾之前,似乎我最初的想法是相当准确的,并且被我的测试再次印证。这个事实让我想起了当年我在学校学到的爱因斯坦和他的光电效应的实验,他面临着一个光的波粒二重性的问题,最初的结论是实验受到他的心理状态的影响,即当他期望结果是一个波的时候结果就会是一个波,反之亦然。

也就是说,我坚信我的结果不会在不久的将来被证明二重性,虽然我的心理状态可能在某种程度上对它们有影响。

关于比较

上面我读过一份材料具有一种革新的方式,在我看来,需要了解其自然而然的主观性和作者自身的偏见。

我决定采用这种方式,因此,提前声明以下内容:

开发者花了很多年来打磨他们的作品。那些取得了更高成就的人通常参考很多因素来做出自己的抉择,这是主观的做法;你需要推崇和捍卫你的技术决策。

也就是说,这个比较文章的着眼点不会成为另一篇“哥们,使用适合你的东西就好”的口水文章。我将会根据我的自身经验、需求和偏见提出建议。你可能会同意其中一些观点,反对另外一些;这很好——你的意见会帮助别人做出明智的选择。

感谢 SitePoint 的 Craig Buckler ,重新启发了我对比较类文章的看法——尝试重新忘记自我,并试图让所有的读者心悦诚服。

关于测试

所有的测试都在本地运行:

  • 英特尔酷睿 i7-2600k,四核八线程的机器
  • Gentoo Linux 是用于测试的操作系统

用于基准测试的工具:ApacheBench,2.3 <$Revision: 1748469 $>

测试包括一系列基准,从 1000 到 10000 个请求以及从 100 到 1000 个的并发请求——结果相当令人惊讶。

此外,我还进行了在高负载下测量服务器功能的压力测试。

至于内容,主要是一个包含一些 Lorem Ipsum 的标题和一张图片静态文件。

Lorem Ipsum 和 ApacheBenchmark

我决定专注于静态文件的原因是因为它们去除了可能对测试产生影响的各种渲染因素,例如:编程语言解释器的速度、解释器与服务器的集成程度等等。

此外,基于我自身的经验,平均网页加载时间很大一部分通常花费在静态内容上,例如图片,因此关注哪个服务器可以节省我们加载静态内容的时间是比较现实的。

除此之外,我还想测试一个更加真实的案例,案例中我在运行不同 CMS 的动态页面(稍后将详细介绍)时对服务器进行基准测试。

服务器

正如我用的是 Gentoo Linux,你就知道我的 HTTP 服务器在一开始就已经经过优化了,因为我在构建系统的时候只使用了我实际需要的东西。也就是说,当我运行我的测试的时候,不会在后台运行任何不必要的代码或加载没用的模块。

Apache、Nginx 和 Node.js 的使用的配置对比

Apache

$: curl -i http://localhost/index.html

HTTP/1.1 200 OK
Date: Sun, 30 Oct 2016 15:35:44 GMT
Server: Apache
Last-Modified: Sun, 30 Oct 2016 14:13:36 GMT
ETag: "2cf2-54015b280046d"
Accept-Ranges: bytes
Content-Length: 11506
Cache-Control: max-age=600
Expires: Sun, 30 Oct 2016 15:45:44 GMT
Vary: Accept-Encoding
Content-Type: text/html

Apache 配置了 “event mpm”。

Nginx

$: curl -i http://localhost/index.html

HTTP/1.1 200 OK
Server: nginx/1.10.1
Date: Sun, 30 Oct 2016 14:17:30 GMT
Content-Type: text/html
Content-Length: 11506
Last-Modified: Sun, 30 Oct 2016 14:13:36 GMT
Connection: keep-alive
Keep-Alive: timeout=20
ETag: "58160010-2cf2"
Accept-Ranges: bytes

Nginx 包括几个调整:sendfile ontcp_nopush ontcp_nodelay on

Node.js

$: curl -i http://127.0.0.1:8080

HTTP/1.1 200 OK
Content-Length: 11506
Etag: 15
Last-Modified: Thu, 27 Oct 2016 14:09:58 GMT
Content-Type: text/html
Date: Sun, 30 Oct 2016 16:39:47 GMT
Connection: keep-alive

在静态测试中使用的 Node.js 服务器是从头定制的,这样可以让它尽可能更加的轻快——没有使用外部模块(Node 核心模块除外)。

测试结果

点击图片以放大:

Apache、Nginx 与 Node 的对比:请求负载的性能(每 100 位并发用户)

Apache、Nginx 与 Node 的对比:用户负载能力(每 1000 个请求)

压力测试

Apache、Nginx 与 Node 的对比:完成 1000 位用户并发的 100000 个请求耗时

我们可以从结果中得到什么?

从以上结果判断,似乎 Nginx 可以在最少的时间内完成最多请求,换句话来说,Nginx 是最快的 HTTP 服务器。

还有一个相当惊人的事实是,在特定的用户并发数和请求数下,Node.js 可以比 Nginx 和 Apache 更快。

但当请求的数量在并发测试中增加的时候,Nginx 将重回领先的位置,这个结果可以让那些陷入 Node.js 的遐想的人清醒一下。

和 Apache、Nginx 不同的是,Node.js 似乎对用户的并发数不太敏感,尤其是在集群节点。如图所示,集群节点在 0.1 秒左右保持一条直线,而 Apache 和 Nginx 都有大约 0.2 秒的波动。

基于上述统计可以得出的结论是:网站比较小,其使用的服务器就无所谓。然而,随着网站的受众越来越多,HTTP 服务器的影响变得愈加明显。

当涉及到每台服务器的原始速度的底线的时候,正如压力测试所描述的,我的感觉是,性能背后最关键的因素不是一些特定的算法,而实际上是运行的每台服务器所用的编程语言。

由于 Apache 和 Nginx 都使用了 C 语言—— AOT 语言(编译型语言),而 Node.js 使用了 JavaScript ——这是一种 JIT 语言(解释型语言)。这意味着 Node.js 在执行程序的过程中还有额外的工作负担。

这意味着我不能仅仅基于上面的结果来下结论,而要做进一步校验,正如你下面看到的结果,当我使用一台经过优化的 Node.js 服务器与流行的 Express 框架时,我得到几乎相同的性能结论。

全面考虑

逝者如斯夫,如果没有服务的内容,HTTP 服务器是没什么用的。因此,在比较 web 服务器的时候,我们必须考虑的一个重要的部分就是我们希望在上面运行的内容。

虽然也有其它的功能,但是 HTTP 服务器最广泛的使用就是运行网站。因此,为了看到每台服务器的性能的实际效果,我决定比较一下世界上使用最广泛的 CMS(内容管理系统)WordPress 和 Ghost —— 内核使用了 JavaScript 的一颗冉冉升起的明星。

基于 JavaScript 的 Ghost 网页能否胜过运行在 PHP 和 Apache / Nginx 上面的 WordPress 页面?

这是一个有趣的问题,因为 Ghost 具有操作工具单一且一致的优点——无需额外的封装,而 WordPress 需要依赖 Apache / Nginx 和 PHP 之间的集成,这可能会导致显著的性能缺陷。

除此之外,PHP 距 Node.js 之间还有一个显著的性能落差,后者更佳,我将在下面简要介绍一下,可能会出现一些与初衷大相径庭的结果。

PHP 与 Node.js 的对决

为了比较 WordPress 和 Ghost,我们必须首先考虑一个影响到两者的基本组件。

基本上,WordPress 是一个基于 PHP 的 CMS,而 Ghost 是基于 Node.js(JavaScript)的。与 PHP 不同,Node.js 有以下优点:

  • 非阻塞的 I/O
  • 事件驱动
  • 更新颖、更少的残旧代码

由于有大量的测评文章解释和演示了 Node.js 的原始速度超过 PHP(包括 PHP 7),我不会再进一步阐述这个主题,请你自行用谷歌搜索相关内容。

因此,考虑到 Node.js 的性能优于 PHP,一个 Node.js 的网站的速度要比 Apache / Nginx 和 PHP 的网站快吗?

WordPress 和 Ghost 对决

当比较 WordPress 和 Ghost 时,有些人会说这就像比较苹果和橘子,大多数情况下我同意这个观点,因为 WordPress 是一个完全成熟的 CMS,而 Ghost 基本上只是一个博客平台。

然而,两者仍然有共同竞争的市场,这两者都可以用于向世界发布你的个人文章。

制定一个前提,我们怎么比较两个完全基于不同的代码来运行的平台,包括风格主题和核心功能。

事实上,一个科学的实验测试条件是很难设计的。然而,在这个测试中我对更接近生活的情景更感兴趣,所以 WordPress 和 Ghost 都将保留其主题。因此,这里的目标是使两个平台的网页大小尽可能相似,让 PHP 和 Node.js 在幕后斗智斗勇。

由于结果是根据不同的标准进行测量的,最重要的是尺度不一样,因此在图表中并排显示它们是不公平的。因此,我改为使用表:

Node、Nginx、Apache 以及运行 WordPress 和 Ghost 的比较。前两行是 WordPress,底部的两行是 Ghost

正如你所见,尽管事实上 Ghost(Node.js)正在加载一个更小的页面(你可能会惊讶 1kb 可以产生这么大的差异),它仍然比同时使用 Nginx 和 Apache 的 WordPress 要慢。

此外,使用 Nginx 代理作为负载均衡器来接管每个 Node 服务器的请求实际上会提升还是降低性能?

那么,根据上面的表格,如果说它产生什么效果的话,它造成了更慢的效果——这是一个合理的结果,因为额外封装一层理所当然会使其变得更慢。当然,上面的数字也表明这点差异可以忽略不计。

但是上表中最重要的一点是,即使 Node.js 比 PHP 快,HTTP 服务器的作用也可能超过某个 web 平台使用的编程语言的重要性。

当然,另一方面,如果加载的页面更多地依赖于服务器端的脚本处理,那么我怀疑结果可能会有点不同。

最后,如果一个 web 平台真的想在这场竞赛里击败 WordPress,从这个比较中得出的结论就是,要想性能占优,必须要定制一些像 PHP-FPM 的工具,它将直接与 JavaScript 通信(而不是作为服务器来运行),因此它可以完全发挥 JavaScript 的力量来达到更好的性能。


via: https://iwf1.com/apache-vs-nginx-vs-node-js-and-what-it-means-about-the-performance-of-wordpress-vs-ghost/

作者:Liron 译者:OneNewLife 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

(题图来自:deviantart.net