我遇到了 CPU 限制问题,该问题似乎仅在运行特定工作负载时才会触发Kythe 索引器。问题末尾有详细的重现步骤。我将在这里给出一个高层次的总结。
Kythe 是一个从源代码中提取索引的工具。我在 GNU Parallel 下为 LLVM 中的每个编译单元运行 Kythe(并行将自动运行 32 个进程)。
以下工作负载能够持续最大化所有核心 10 分钟以上:
- 使用 Ninja 进行 Clang 编译。此工作负载有点类似于索引,因为它应该执行类似数量的输入操作; Kythe 在内部使用 Clang 来索引代码。 CPU温度徘徊在75C - 80C左右。与运行 Kythe 的一个可能不相关的区别是 Kythe 可以生成大约 300MB ~ 2.5GB 的索引每编译单元,所以我在一个小的 Python 包装器下运行 Kythe,该包装器创建一个临时文件,让 Kythe 写入它,然后删除该文件。
- GNU Parallel 运行一个简单的繁忙循环(在 5-15 秒内按元素递增 1K 向量,这大约是 Kythe 索引编译单元所需的时间)。温度与上面类似。
然而,在 GNU Parallel 下运行 Kythe 会导致某种限制,CPU 降频,并且工作没有分配(使用sudo cpupower frequency-set -g performance
没有帮助——所以问题似乎是 Kythe 进程在一段时间后受到惩罚/降低优先级) ,浏览器也会看到速度变慢)。温度降至60℃左右。
在上图中,图表的前半部分显示了 GNU Parallel/Busy 循环工作负载。然后,我终止这些进程并启动 GNU Parallel/Kythe 工作负载。由于某种原因,Kythe 工作负载会遇到限制问题,而 Clang 和 Busy 循环工作负载都不会遇到这些问题。可能是什么原因造成的/我如何进一步调试?
复制步骤
(准备)在 LLVM 存储库上运行 CMake 命令:
git clone https://github.com/llvm/llvm-project.git --depth=1 cd llvm-project/llvm CC=/usr/bin/clang-14 CXX=/usr/bin/clang++-14 cmake -B ../build -DCMAKE_BUILD_TYPE=Release -G Ninja -DLLVM_ENABLE_PROJECTS=clang
这将准备一个
../build/compile_commands.json
文件。(准备)下载 Kythe 版本(例如在 下
$HOME/code
)并按照 Kythe 文档中的描述运行提取器。wget https://github.com/kythe/kythe/releases/download/v0.0.60/kythe-v0.0.60.tar.gz -o $HOME/code tar xzf $HOME/code/kythe-v0.0.60.tar.gz cd ../build mkdir kythe-v0.0.60-output KYTHE_ROOT_DIRECTORY=$PWD KYTHE_OUTPUT_DIRECTORY=$PWD/kythe-v0.0.60-output/ KYTHE_CORPUS=my-llvm $HOME/code/kythe-v0.0.60/tools/runextractor compdb -extractor $HOME/code/kythe-v0.0.60/extractors/cxx_extractor
(实际工作负载)并行运行Kythe:
#!/usr/bin/env python3 # code/timing.py import sys import tempfile import time import subprocess import os from datetime import datetime input_file = sys.argv[1] _, output_file = tempfile.mkstemp(prefix="entries-") start = datetime.now() subprocess.run(["/home/varun/code/kythe-v0.0.60/indexers/cxx_indexer", "--ignore_unimplemented", input_file, "-o", output_file]) end = datetime.now() delta = end - start input_size = os.stat(input_file).st_size output_size = os.stat(output_file).st_size print("{} bytes to {} bytes in {} sec from {}".format(input_size, output_size, delta.seconds, input_file)) os.remove(output_file)
parallel ~/code/timing.py ::: kythe-v0.0.60-output/*.kzip | tee timings.txt
答案1
与运行 Kythe 的一个可能不相关的区别是,Kythe 可以为每个编译单元生成大约 300MB ~ 2.5GB 的索引,因此我在一个小型 Python 包装器下运行 Kythe,该包装器创建一个临时文件,让 Kythe 对其进行写入,然后删除文件。
事实证明,这是非常相关的。通过创建一个模拟 Kythe 的高磁盘输出的虚拟脚本并查看它是否会导致类似的限制,可以轻松检查这是否导致问题。这是一个示例脚本:
#!/usr/bin/env python3
import sys
import tempfile
import time
import os
import random
from datetime import timedelta
from datetime import datetime
output_fd, output_file = tempfile.mkstemp(prefix="entries-")
size_100M = random.randint(5, 15)
start = datetime.now()
# Kythe can end up writing about 500MB - 1.5GB in a span of 5-15s.
# We mimic that workload by writing 1M every 0.01s, and just wasting
# some CPU if we're done writing early.
dummyvec = list(range(1024))
for i in range(size_100M * 100):
iter_start = datetime.now()
iter_end = iter_start + timedelta(milliseconds=10)
os.write(output_fd, os.urandom(1024 * 1024))
while datetime.now() < iter_end:
# Waste CPU
for i in range(len(dummyvec)):
dummyvec[i] = (dummyvec[i] + 1) % 1024
end = datetime.now()
delta = end - start
output_size = os.stat(output_file).st_size
print("Wrote {} bytes in {} sec".format(output_size, delta.seconds))
os.remove(output_file)
该脚本可以再次运行parallel
:
parallel high_output.py ::: kythe-v0.0.60-output/*.kzip
如果您开始看到超过 15 秒的计时,则可以肯定发生了限制。
事实证明,在parallel
命令运行一段时间后,我开始看到超过 20-25 秒的时间。
查看其他温度也很重要,而不仅仅是 CPU 温度。例如,您可以lm-sensors
在 Ubuntu 上使用该软件包来查看所有不同传感器的温度。
# Re-run sensors from lm-sensors every 2 seconds
watch -n 2 sensors
事实证明,问题出在 NVMe 驱动器的温度上。
nvme-pci-0400
Adapter: PCI adapter
Composite: +55.9°C (low = -273.1°C, high = +81.8°C)
(crit = +84.8°C)
Sensor 1: +55.9°C (low = -273.1°C, high = +65261.8°C)
Sensor 2: +87.8°C (low = -273.1°C, high = +65261.8°C)
在全速运行中,Kythe 最终可以为每个编译单元写入 100MB/s 的输出。虽然这还不错,但拥有 32 个进程意味着 3.2GB/s 的输出,在我的情况下这似乎压垮了 NVMe,导致过热。当 NVMe 驱动器过热时,Linux 似乎会限制所有正在运行的进程(解释了浏览器速度变慢的原因)。
特别是对于 Kythe,基于此Google 群组话题,Kythe 有一个标志--experimental_dynamic_claim_cache
,看起来它可以通过在后台利用 memcached 来帮助减少磁盘输出。