pg_test_timing — 测量计时开销
pg_test_timing [option...]
pg_test_timing 是一个用于测量你系统上计时开销的工具,并且可以确认系统时间从不会倒退。收集计时数据缓慢的系统可能给出不太准确的 EXPLAIN ANALYZE 结果。
pg_test_timing 接受下列命令行选项:
-d duration--duration=duration指定测试的持续时间(以秒为单位)。持续时间越长,精度会略高一些,也更有可能发现系统时钟回退的问题。默认的测试持续时间为 3 秒。
-V--version打印 pg_test_timing 的版本并退出。
-?--help显示关于 pg_test_timing 命令行参数的帮助并退出。
好的结果会显示大多数(>90%)单独的计时调用耗时不到一微秒。平均每次循环的开销会更低,低于 100 纳秒。这个来自使用 TSC 时钟源的 Intel i7-860 系统的示例展示了出色的性能:
Testing timing overhead for 3 seconds.
Per loop time including overhead: 35.96 ns
Histogram of timing durations:
< us % of total count
1 96.40465 80435604
2 3.59518 2999652
4 0.00015 126
8 0.00002 13
16 0.00000 2
注意,每次循环时间所使用的单位与直方图所使用的单位不同。循环可以达到几纳秒(ns)以内的分辨率,而单独的计时调用只能分辨到一微秒(us)。
当查询执行器使用 EXPLAIN ANALYZE 运行一个语句时,会在显示摘要的同时对单独的操作进行计时。可以使用 psql 程序通过统计行数来检查你系统的开销:
CREATE TABLE t AS SELECT * FROM generate_series(1,100000); \timing SELECT COUNT(*) FROM t; EXPLAIN ANALYZE SELECT COUNT(*) FROM t;
被测量的 i7-860 系统运行该计数查询用时 9.8 毫秒,而 EXPLAIN ANALYZE 版本用时 16.6 毫秒,两者都处理了刚超过 100,000 行。6.8 毫秒的差距意味着每行的计时开销是 68 纳秒,约为 pg_test_timing 所估计值的两倍。即便是这样相对较小的开销,也使得完全计时的计数语句多花了几乎 70% 的时间。对于更庞大的查询,计时开销带来的问题会小一些。
在一些较新的 Linux 系统上,可以随时更改用于收集计时数据的时钟源。第二个示例展示了在上面用于产生快速结果的同一系统上,切换到较慢的 acpi_pm 时间源可能带来的速度变慢:
# cat /sys/devices/system/clocksource/clocksource0/available_clocksource
tsc hpet acpi_pm
# echo acpi_pm > /sys/devices/system/clocksource/clocksource0/current_clocksource
# pg_test_timing
Per loop time including overhead: 722.92 ns
Histogram of timing durations:
< us % of total count
1 27.84870 1155682
2 72.05956 2990371
4 0.07810 3241
8 0.01357 563
16 0.00007 3
在这一配置下,上面示例的 EXPLAIN ANALYZE 需要 115.9 毫秒。也就是 1061 纳秒的计时开销,同样是该工具直接测得值的某个较小的倍数。这么多的计时开销意味着实际查询本身只占计入时间的很小一部分,大部分时间都被开销消耗掉了。在这一配置下,任何涉及大量被计时操作的 EXPLAIN ANALYZE 总时间都会因计时开销而显著膨胀。
FreeBSD 也允许在运行中更改时间源,并且它会在启动期间记录有关所选定时器的信息:
# dmesg | grep "Timecounter" Timecounter "ACPI-fast" frequency 3579545 Hz quality 900 Timecounter "i8254" frequency 1193182 Hz quality 0 Timecounters tick every 10.000 msec Timecounter "TSC" frequency 2531787134 Hz quality 800 # sysctl kern.timecounter.hardware=TSC kern.timecounter.hardware: ACPI-fast -> TSC
其他系统可能只允许在启动时设置时间源。在较老的 Linux 系统上,“clock” 内核设置是进行这类更改的唯一方法。甚至在一些较新的系统上,你能看到的时钟源选项也只有 “jiffies”。Jiffies 是较老的 Linux 软件时钟实现,当它有足够快的计时硬件作为支撑时,它可以有良好的分辨率,如下例所示:
$ cat /sys/devices/system/clocksource/clocksource0/available_clocksource
jiffies
$ dmesg | grep time.c
time.c: Using 3.579545 MHz WALL PM GTOD PIT/TSC timer.
time.c: Detected 2400.153 MHz processor.
$ pg_test_timing
Testing timing overhead for 3 seconds.
Per timing duration including loop overhead: 97.75 ns
Histogram of timing durations:
< us % of total count
1 90.23734 27694571
2 9.75277 2993204
4 0.00981 3010
8 0.00007 22
16 0.00000 1
32 0.00000 1
收集准确的计时信息通常是在使用具有不同精度级别的硬件时钟的计算机上完成的。对于某些硬件,操作系统几乎可以把系统时钟时间直接传递给程序。系统时钟也可以由一个仅仅提供计时中断(即以某个已知时间间隔发出的周期性节拍)的芯片派生而来。无论哪种情况,操作系统内核都会提供一个隐藏这些细节的时钟源。但是该时钟源的精度以及它能多快地返回结果,则取决于底层硬件。
不准确的计时可能导致系统不稳定。对时钟源的任何更改都要非常仔细地进行测试。操作系统的默认值有时是为了可靠性而非最佳精度而设定的。另外,如果你正在使用虚拟机,请研究与之兼容的推荐时间源。虚拟硬件在模拟定时器时面临额外的困难,厂商通常会针对每种操作系统给出建议的设置。
时间戳计数器(TSC)时钟源是当前一代 CPU 上可用的最准确的时钟源。当操作系统支持它并且 TSC 时钟可靠时,它是跟踪系统时间的首选方式。TSC 可能在若干方面无法提供准确的计时源,从而变得不可靠。较老的系统可能有随 CPU 温度而变化的 TSC 时钟,使其无法用于计时。在一些较老的多核 CPU 上尝试使用 TSC 可能得到在多个核之间不一致的报告时间。这可能导致时间倒退,本程序会检查这一问题。即使是最新的系统,在非常激进的节能配置下也可能无法提供准确的 TSC 计时。
较新的操作系统可能会检查已知的 TSC 问题,并在发现这些问题时切换到较慢但更稳定的时钟源。如果你的系统支持 TSC 时间但并不默认使用它,那么它可能是出于某种充分的理由而被禁用的。另外,有些操作系统可能无法正确检测所有可能的问题,或者即使在使用 TSC 已知不准确的情形下也允许使用它。
高精度事件定时器(HPET)在它可用而 TSC 不准确的系统上是首选的定时器。该定时器芯片本身可通过编程实现高达 100 纳秒的分辨率,但你在系统时钟中可能看不到那么高的精度。
高级配置与电源接口(ACPI)提供一个电源管理(PM)定时器,Linux 将其称为 acpi_pm。由 acpi_pm 派生的时钟最多能提供 300 纳秒的分辨率。
较老的 PC 硬件上使用的定时器包括 8254 可编程间隔定时器(PIT)、实时时钟(RTC)、高级可编程中断控制器(APIC)定时器以及 Cyclone 定时器。这些定时器以毫秒级分辨率为目标。