> For the complete documentation index, see [llms.txt](https://freebsd-journal-cn.bsdcn.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://freebsd-journal-cn.bsdcn.org/20150506-ce-liang-liang-ci-dai-ma-yi-ci-xie-hao/measure-twice-code-once.md).

# 测量两次，代码一次写好

* 原文：[Measure Twice Code Once !](https://freebsdfoundation.org/our-work/journal/browser-based-edition/measure-twice-code-once/)
* 作者：**Jim Thompson**、**George V. Neville-Neil**

## FreeBSD 网络性能分析

自互联网诞生以来，任何操作系统的网络子系统都随着所支持的协议与特性集合的增长而日趋复杂。防火墙、虚拟专用网（VPN）与 IPv6 只是 FreeBSD 内核中具备的若干特性——30 多年前在加州大学伯克利分校开发最初 BSD 发行版时，这些特性甚至都未被设想。网络硬件的进步——10 Gbps 网卡只需几百美元即可获得——已远超内核网络软件最初编写时所针对的速度。与过去 30 年处理器速度的提升类似，系统开发者与集成商始终依赖下一代硬件来解决当前一代的性能瓶颈，往往不借助任何系统的测量手段。本文向各熟练等级的开发者与系统集成者展示如何对网络系统做基准测试，并配以我们在 FreeBSD 内核上的经验实例。文中点出并解决常见陷阱，给出一组代表性测试。本工作的另一成果是简单的网络测试协调系统 Conductor，文中也一并介绍。Conductor 系统连同所有测试与结果，并行发布于两个开源项目：<https://github.com/gvnn3/conductor> 与 <https://github.com/gvnn3/netperf>。

## 网络基准测试很难

阅读任何公司公布的基准测试都会让合格的工程师心生怀疑——理应如此。创建广泛适用且诚实的基准测试是一项艰巨工作，而许多基准测试并非意在公正。有些恰恰相反，意在让作者的系统以尽可能好的面貌示人。基准测试之所以被选中并推广，往往是因为它第一个展示了作者所认为的、被测系统的“良好性能”。

开发基准测试的技术挑战并不限于避免对读者说谎。要弄清如何在动态系统中可靠且可重复地测量某物，需要对所有系统组件及其交互如何联手制造假测量有深刻理解。非联网系统——其各组件间的互联与交互可视或本应可视——仍复杂到足以让许多开发者栽跟头。联网系统更难测量，原因包括异步性、可见性、缺乏良好同步的时钟。

与计算机系统的其他方面不同，网络被理解为不可靠，高层协议提供可靠性、有序等特性，而单台主机上运行的软件则视这些为理所当然。网络栈多层都接受不可靠性，使得仅从发送方或接收方单一观察点测量性能变得困难。要理解网络性能，我们必须在系统中多个点观察：发送方、接收方、视网络应用而定，存在于两端之间的任何系统。在单台主机上测量某操作时，我们可用主机时钟测量操作完成所花时间。跨网络同步时钟的困难意味着，我们无法像软件在单台主机上那样可靠地测量操作延迟。在一台主机上记录数据包发送时刻，再在另一台主机上记录其接收时刻，其可靠性仅与两台主机间的同步程度相当。两台或更多主机可借助网络时间协议（NTP）同步到毫秒级，或借助精密时间协议（PTP）同步到微秒级，但大多数联网系统在大规模部署中无法使用 PTP，即便可以，现代数据中心常见的 10 Gbps 网络的延迟也在数十微秒的低端，使主机间精度难以确定。

精确计时只是面临网络基准测试的工程师所要应对的问题之一。第一个问题仅仅是理解该测什么。有些应用对延迟敏感，有些对丢包敏感，还有些需要大带宽。弄清这三个维度中哪个对应用最重要，是基准测试开始前必须完成的首要任务。对此及其他性能测量主题的全面论述，是优秀著作 \[3] 的主题。

本文中我们讨论一些非常基础的基准测试技术，并展示它们如何在若干系统上执行——先看 FreeBSD 中的数据包转发 \[2]，再扩展到四个防火墙的对比。本工作的目标不是为某次具体测量给出定论性结果，而是展示我们如何构建基准测试、如何用同样的技术来测量和改进网络性能。

## 理解现代硬件

网络硬件早已突破 1 Gbps，10 Gbps 及更高速率连最小的公司也触手可及。虽然网络速度持续攀升，处理器速度却止步于约 3 GHz，内存总线则更贴近处理器速度的增长。这些速度失配催生了这样的系统架构：试图把工作分摊到许多相似部件上来完成一项工作。多核、多内存总线、多网络队列都会在现代硬件上做基准测试时引入测量噪声。

要达到顶级性能，系统的所有资源——包括 CPU 核与缓存、内存、网络队列——必须对齐。当具有相同四元组（源 IP、目的 IP、源端口、目的端口）的数据包始终由同一核、同一缓存、同一队列处理时，资源即处于对齐状态。为什么这种对齐是必需的？

高速网络——目前指任何带宽 10 Gbps 或更高的网络——以如此高的速率处理数据包，CPU 几乎没有时间对数据包做任何工作。在 10 Gbps 下，最小尺寸的数据包（64 字节）大约以每秒 1400 万个到达。接收这些数据包的系统有 67 纳秒来处理每个。在这段时间内，一颗 3 GHz 处理器大约有 200 个周期来处理每个数据包。这似乎足够，直到把访问内存与缓存的代价纳入考量。一次缓存未命中可能花费 32ns，这意味着每当一个数据包到达，而处理该数据包所需的相关数据不在数据包最终路由到的核上时，系统需要从缓存中驱逐数据，再从内存中取回数据。与处理数据包相关的数据包括转发与路由表项、套接字缓冲区、内核中所有与数据包相关的加锁数据结构。缓存未命中的代价如今已高到，在处理高速数据包流时，任何此类进程都必须绑定到单一 CPU 核才能达到最高性能。让调度器决定进程最佳执行位置，会导致数据包转发速率波动、整体性能下降。

要从高速网卡与现代硬件中获得全部性能，唯一途径是了解完整的系统拓扑。数据包必须到达并离开一张网卡，该网卡所在的总线连接到运行应用的那颗 CPU。如果应用在核间迁移而失去缓存局部性，性能会受损。在 FreeBSD 上，这意味着网络应用必须用 `cpuset` 程序或系统调用绑定到某个处理器核，才能以最高速度工作。不把网络程序绑定到核，是工程师在高速网络硬件上工作时最常犯的错误。

多路系统——那些有两颗或更多多核 CPU 的系统——因处理器间通信的高代价而加剧了这一问题。自 QPI 出现以来生产的任何基于 Intel 的多路系统（包括过去两年生产的所有系统）都把每个设备（如网络控制器）至多连接到系统中的一颗 CPU。当一张 10 Gbps 网卡插到总线上时，它只靠近其中一颗 CPU。如果应用在来自另一颗 CPU 的 NIC 上处理数据包，代价高到没必要为额外的 CPU 花那份钱。

## 测试自动化

为开展工作，我们创建了简单的协调程序与协议——Conductor，可用于在无需人工干预的情况下设置、运行和重置系统。

Conductor 系统是一小套 Python 库，用于在测试期间协调各系统。典型测试设置见图 1，其中三台主机用于测试数据包转发。主机 lynx1 通过 rabbit3（DUT）的 cxl0 接口向 rabbit3 发送数据包。数据包由 rabbit3 从其 cxl1 接口转发出去，由 lynx3 计数。Conductor 用于协调这些过程。Conductor 从一台主控主机 zoo 上运行，zoo 位于管理网络上，图中未显示。Conductor 联系每台主机上的 player 程序，分四个阶段执行命令，以可复现的方式运行测试场景。Setup 阶段加载设备驱动、设置路由表项，并用初始 ping 命令确保 ARP 表为测试做好准备。设置完成后，Run 阶段启动数据包生成器的源与汇。可以给命令设置超时，这样没有内置超时的程序也能在预设秒数后被强制退出。Collection 阶段复制测试结果，包括日志文件、数据包捕获、其他与测试相关的输出。最后，Shutdown 阶段移除 Setup 阶段加载到内核的路由与驱动，把系统恢复到测试前的初始状态。

## 内核数据包转发

FreeBSD 操作系统中，通过基础网络栈、各种防火墙转发数据包这一领域，正适合性能分析与改进。数据包转发是性能工作的良好候选，原因有几方面。首先，有简单、定量的性能度量——每秒数据包数。其次，工作负载的生成与测量都在被测系统（我们称之为 Device Under Test，DUT）之外。数据包的外部生成与接收意味着 DUT 只做我们在测试中关心的工作，而不是把周期耗费在开销上（见图 2）。

自 BSD 最早版本以来，该操作系统既能作为端系统，也能作为路由器转发数据包，常作为小型办公室路由器。FreeBSD 还包括两个著名的防火墙子系统。IPfw 最初由 Luigi Rizzo 为 FreeBSD 编写 \[4]，在过去 20 年里得到增强与扩展。PF 防火墙由 OpenBSD 开发，后来移植到 FreeBSD。PF 并非为 SMP 内核编写，但由 Gleb Smirnoff 等人更新，使其在 FreeBSD 内核中无需 Giant lock 即可运行。IPfw 与 PF 的功能集不完全相同，但都实现了可在接口间放行或丢弃流量的防火墙。

在对防火墙软件做任何测量之前，需要对所用系统建立基线。我们进行了若干实验，确定内核在不施加防火墙所需工作或内核自身工作的情况下能多快转发数据包。实验在 Sentex 测试实验室的三台系统上进行——lynx1、lynx3 与 rabbit3。Lynx1 充当流量源，经 rabbit3 向 lynx3 发送数据包。两台 lynx 机器都是双路，配备 10 核 E5-2680 Xeon 处理器，主频 2.8 GHz。Rabbit3 是单路四核 E5-2637 Xeon，主频 3 GHz。Lynx1 的第二个 10G 端口直接连到 rabbit3 的第一个 10G 端口，lynx3 的第二个 10G 端口直接连到 rabbit3 的第二个 10G 端口，如图 1 所示。

最简单也最常见的网络测试之一是单流 TCP 数据包。我们用 `iperf3` 程序在测试实验室内为 TCP 建立了简单基线。每次测试持续 10 秒，带宽几乎无变化。图 2 所示输出是 `iperf` 的输出，每秒报告一次整个 10 秒运行的带宽。

仅用 TCP 测试网络设备有许多理由不这么做。与其他协议不同，TCP 试图尽可能高效地使用底层网络，并在出现错误时调整行为。虽然这对普通用户是好事，但它不会暴露某款网络软件或设备的粗糙之处。作为简单基线，它是不错的起点，但有误导性，因为这一项测试可能让我们以为系统能在所有情况下转发 10 Gbps，实则不然。

测试负责转发数据包的系统（如交换机、路由器和防火墙）时，我们要测量能跨越 DUT 的数据包数量。此类测试使用网络层数据包，其中传输协议（如 TCP 或 UDP）大多无关紧要。在通过 DUT 传递数据包之前，我们在两块背靠背连接的网卡之间建立了原始基线。10G 网络理论上每秒可传输略多于 1400 万个 64 字节数据包。我们想确定在加入任何其他软件层之前，我们的系统能发送和接收多少数据包。我们用 netmap \[5] 的 `pkt-gen` 程序从 lynx1 经背靠背链路向 rabbit3 发送 64 字节数据包。源端的部分输出见图 3，目的端见图 4。我们用 `pkt-gen` 是因为它使用 **netmap(4)** 驱动，让我们能以接近线速发送和接收数据包。

初始测试表明，DUT 无法在其 10G 接口上完全吸收最小尺寸的以太网帧（64 字节）。在源与 DUT 之间运行一系列数据包表明，128 字节或更长的数据包都能被 `pkt-gen` 正确接收。完整的数据包尺寸扫描结果见表 1。通过使用 `pkt-gen` 与 netmap，我们能够直接向网卡写数据包并直接从网卡读数据包，而数据包不经过操作系统的任何网络处理软件，从而为底层硬件与驱动能达到的能力给出了良好的基线测量。一旦为网卡与主机建立了基线，我们就测量操作系统在接口间转发数据包的能力。

转发场景下，从 lynx1 经 rabbit3 向 lynx3 发送单流 UDP/IP 数据包并在 lynx3 处计数。Rabbit3 上除操作系统外不运行任何软件，其唯一工作就是在图 1 所示的 cxl0 与 cxl1 接口间转发数据包。

我们的第二组测量使用多种数据包尺寸，包括 64、256、512 和 1500 字节。每次测量运行 30 秒，然后数据通过 `ministat` 处理以找出中位转发速率。Jumbo frame（大于 1500 字节的数据包）未测试。数据包转发以每秒数据包数（PPS）衡量，与原始带宽的关系是 PPS 乘以数据包大小。

数据包转发实验结果见表 2。我们观察到，一旦操作系统参与数据包转发，除全尺寸以太网帧外，我们的 PPS 测量值都急剧下降。对于最小尺寸的帧（64 字节），操作系统内核只能转发发给它的数据包的 7%。很少有网络只处理最小尺寸的帧，因为使用 UDP 与 IPv4 时此类帧最多只能承载 10 字节数据。最小尺寸的帧通常只作为 TCP ACK 包出现，不含数据。网络测试常用最小尺寸的帧来展示某款软件或硬件在最坏情况下的表现。随着数据包尺寸增大，系统最终能以线速转发数据包。

这样的数据包转发测试向我们展示了网络基准测试的重要方面：原始带宽并不总是网络性能的最佳度量。了解测量中有多少开销很重要。数据包越小，传输等量数据的开销越大。以线速转发最小尺寸数据包的系统，转发的实际数据仍少于以线速转发全尺寸数据包的系统。10 Gbps 网络用 64 字节数据包传输约 1.1 Gbps 数据，因为每个 64 字节数据包有 58 字节包头——以太网 14 字节、IPv4 20 字节、TCP 20 字节、4 字节校验和。每 64 字节数据包 58 字节开销，意味着 6.8 Gbps 带宽用于包头，使用 UDP 时仅剩 10 字节用于实际数据。使用 TCP 时仅剩 6 字节用于用户数据。使用 1500 字节数据包时，9.4 Gbps 带宽是可用数据，仅 354 Mbps 给了开销。

### 为什么？

通过操作系统内核转发数据包如此之慢的原因之一是，每个流经 `ip_input()`、`ip_forward()`、`ip_output()`（内核的数据包输入、转发与输出例程）的数据包，在交还给外出网络设备之前都要经历大量检查。

通过内核正常数据包转发路径（`ether_input()` 与 `ether_output()` 之间）的调用图见图 5。图 5 中的输出是用 DTrace \[1] 采集的，展示数据包转发期间调用 `ether_output()` 时其上方的栈。通过内核转发数据包时，每个数据包都要在所示每个函数中经历若干测试，每个函数都为转发的每个数据包增加开销。

为改进系统的数据包转发性能，2003 年加入了快速路径例程 `ip_fastforward()`，其中许多检查被推迟到我们知道可以转发数据包之后。任何名义上正确且不发往本机的数据包都直接转发，不再接受进一步检查。在转发场景下，`ip_input()` 与 `ip_forward()` 会检查的许多属性（如数据包选项）从不出现。把这些昂贵操作推迟到典型数据包会被转发之后，降低了每数据包开销，提升了整体性能。

借助 DTrace，我们能通过记录数据包到达 `ether_input()` 与经 `ether_output()` 离开的时刻来测量数据包穿越系统所花时间。我们运行 D 脚本 30 秒来记录这两个调用之间的时间。这 30 秒内，DUT 承受了一连串 512 字节 UDP/IP 数据包——这是表 2 与表 3 中正常与快速转发情况下差异较大的数据包尺寸。

图 7 展示了不启用快速转发（基线情况）下 `ether_input()` 与 `ether_output()` 之间时间的直方图。图 8 展示了同样脚本、同样数据包流下启用快速转发的情况。注意，落入 1024 桶的时间远多于其他任何桶。每数据包处理时间的降低直接带来了表 3 中所示的更高 PPS。

## 防火墙测试

为比较各种防火墙解决方案，我们进行了若干组测量。测试了四个软件栈：FreeBSD 11-CURRENT、pfSense 2.2（基于 FreeBSD 10.1）、OpenBSD 5.6 与 CentOS 7。FreeBSD（<http://www.freebsd.org>）、pfSense（<http://www.pfsense.org>）和 OpenBSD（<http://www.openbsd.org>）都使用 pf 数据包过滤器，CentOS 7 使用 Linux 的 `iptables`。所有测量都在与“内核数据包转发”一节不同的硬件上进行。这组测试选择了通常用于企业防火墙的硬件，以给出更切实际的示例，展示开源防火墙软件能达成什么。DUT 是 C2758，1U 系统，配备单颗 8 核 2.4 GHz Atom 处理器、8G 内存和一块基于 Intel 82599 的 10G 网络接口。源与汇系统是 Xeon HC 机箱 X5680 @ 3.33GHz，配备基于 Intel 82599 的 10G 网络接口，源运行 Linux 与 DPDK 的 `pktgen`，汇运行 FreeBSD 11-CURRENT（NODEBUG 内核）。DUT 故意选了较慢的机器，以确保源能超过它并在测试中给它足够压力。

DUT 以背靠背方式置于源与汇之间，与图 1 中 lynx1、rabbit3、lynx3 的设置类似。所选操作系统内核不含昂贵的调试开销。特别是 FreeBSD 11——这是开发中的活跃主干——使用了 GENERIC-NODEBUG 内核，以便关闭那些有助于调试 SMP 系统的特性（如用于跟踪锁序反转的 WITNESS 系统）。随后通过每个 DUT 发送一系列数据包，并在汇主机处测量（图 8）。

每次测试所用的规则集在功能上创建得几乎相同。OpenBSD 规则集见图 9，pfSense 与 FreeBSD 规则集相同，见图 10。Linux 规则集见图 11。每个规则集把 DUT 设置为对经 10G 接口穿越系统的数据包执行网络地址转换（NAT）。由于 NAT 要求系统修改通过 DUT 的每个数据包，它是软件栈性能的良好测试。

第一组测量测试了 DUT 在仅使用主机八个可用核中的一颗、且禁用 NAT 过滤的情况下转发数据包的能力。不带 NAT 转发数据包为后续测试提供了对照基线。表 4 所示结果给出了 DUT 每核数据包转发能力的概念。这组测试中，pfSense 表现最佳，Linux 最差——pfSense 转发的数据包是 Linux 的两倍多，也是标准 FreeBSD 11 安装的两倍。pfSense 作为防火墙而非通用操作系统构建，我们在此可见它针对自身职责调校得当。

开启过滤后，数据包速率下降，如表 5 所示，每个数据包过滤器都要检查并修改它所见的每个数据包。我们看到 pfSense 仍领先于所有其他系统，而 FreeBSD 11 跌至最后，仅略落后于 Linux。

单核可能是建立扩展目标的有趣方式——也就是说，在完美世界里，如果单核能转发 N 个数据包，那么 M 个核能转发 M × N 个数据包。现实情况下并非如此，原因众多，包括调度开销、缓存污染、中断路由等，这些将在未来工作中涵盖。表 6 展示了每个系统在关闭过滤、使用所有可用 CPU 核时能转发的数量。多核下，我们看到 Linux 与 pfSense 远超 OpenBSD，FreeBSD 也落后于这两位领先者。

看表 6，我们惊讶地发现 OpenBSD 没有任何性能提升。这一糟糕表现的原因在于 OpenBSD 仍是单线程内核，用一把锁保护所有内核数据结构，因此无法利用系统中额外的核。最后一组防火墙测试展示了每个系统在使用所有可用核过滤数据包时的性能。如前所述，OpenBSD 未从多核获得任何优势，甚至略低于其单核性能，可能是多核与缓存效应所致。CentOS 在单核过滤基础上的提升幅度与不过滤时大致相同——分别为 4.7 倍与 4.8 倍加速。pfSense 系统在过滤时的数据包速率提升远大于不过滤时（1.8 倍与 2.2 倍）。

所有结果的完整对比见图 12。每个柱形顶部给出测得的每秒数据包数，X 轴标签指示测量时过滤是开启还是关闭、DUT 使用单核还是多核。

## 结论

正如开头所说，基准测试设计与评估都很困难。现代硬件具有多核、不同的缓存、内存与设备层次结构，使得从一组基准测试中获得可靠数字的过程更加困难。此外，从计算机系统中提取性能信息的工具——如片上硬件性能监控计数器、Intel 的 pcm 与 VTune 工具等——提供的数据要么太多要么太少，且都受探测效应影响：它们的使用会过度影响 DUT。

本文中我们选择了非常简单的工作负载——数据包转发——原因有几方面。首先是测量工具完全在 DUT 之外，位于我们不在乎系统性能的机器上，只要它能生成和吸收所需的网络数据包数量即可。把工具放在 DUT 之外，减少了我们要在系统上控制的变量数。其次，测量值——每秒数据包数——从定量角度看易于理解——越多越好，这使比较各系统（如防火墙）远为容易。

我们本可以、也可能在 DUT 上测量系统资源（如 CPU 利用率），但我们没有，因为我们不关心 DUT 是用了 10% 还是 90% 的 CPU；我们只关心它能转发多少数据包。

正如过去 10 年反复指出的那样，充分利用多核系统很重要，而随着核数增加、CPU 频率不增，这只会更加重要。不应忽视 OpenBSD 的教训。

加入 Conductor 这一分布式系统协调软件，节省了大量时间，让我们能比手工更频繁地运行更多实验。大多数分布式系统测试都涉及繁琐的设置工作，以配置源、汇与 DUT，再收集数据。拥有一套自动化系统辅助我们的工作，是明显的收益。

## 状态与未来计划

netperf 与 Conductor 项目目前都在持续进行中。相关代码仓库既跟踪自动化方面的工作，也跟踪新测试场景与结果。我们打算继续对 BSD 的网络性能做定期调查，观察它们随时间如何变化和改进。

## 致谢

本工作直接得到 Rubicon Communications（Netgate）的支持，它资助了创建 Conductor 系统的工作、比较和改进 FreeBSD 中 IPfw 与 PF 所花的时间。我们特别感谢 Netgate 的 Matthew Smith 在防火墙测试上的帮助。FreeBSD 基金会与 Sentex Communications 支持测试集群，其中包括用于完成本文所述若干测试的高性能网络设备。

## 参考文献

\[1] Cantrill, B.; Shapiro, M.; and Leventhal, A. Dynamic Instrumentation of Production Systems. In USENIX Annual Technical Conference, page 137. (June 2007)

\[2] McKusick, M.; Neville-Neil, G.; and Watson, R. The Design and Implementation of the FreeBSD Operating System. Pearson Education. (2014)

\[3] Jain, R. The Art of Computer Systems Performance Analysis: Techniques for Experimental Design, Measurement, Simulation, and Modeling. Wiley Interscience. (1991)

\[4] Rizzo, L. Dummynet: A Simple Approach to the Evaluation of Network Protocols. SIGCOMM Computer Communication Review, 27(1):31–41. (January 1997)

\[5] Rizzo, L. netmap: A Novel Framework for Fast Packet I/O. Presented as part of the 2012 USENIX Annual Technical Conference, pages 101–112, Boston, Massachusetts. (2012)

***

**Jim Thompson** 在 UNIX 世界里摸爬滚打了太长时间。他知道自己于 1980 年从 Vax 11/780 上的 BSD Unix Release 4.0 起步。他仍然觉得 `echo 'This is not a pipe.' | cat-> /dev/tty` 很有趣。1985 年，他向自由软件项目提交了第一个补丁——把 GNU Emacs 移植到 Convex 向量超级计算机。Jim 拒绝透露其资质，事实上可能根本没有。他与妻子 Jamie 和儿子 Hunter S. Thompson 住在得克萨斯州奥斯汀附近的一处封闭式院落里。他的电子邮件地址是 <jim@netgate.com>。

**George V. Neville-Neil** 出于兴趣与谋生从事网络与操作系统代码工作。他也教授各种与编程相关的课程。他的兴趣领域是代码探索、操作系统、网络与时间协议。他与 Marshall Kirk McKusick、Robert N. M. Watson 合著了《FreeBSD 操作系统设计与实现》。10 多年来，他是更广为人知的专栏作家 Kode Vicious。他在波士顿的东北大学获得计算机科学学士学位，是 ACM、Usenix Association 与 IEEE 的会员。他热爱自行车与旅行，现居纽约市。

本文以另一种形式发表于 AsiaBSDCon 2015 <https://2015.asiabsdcon.org/>。


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://freebsd-journal-cn.bsdcn.org/20150506-ce-liang-liang-ci-dai-ma-yi-ci-xie-hao/measure-twice-code-once.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
