为什么较高的 CPU 使用率会减慢任务速度?

为什么较高的 CPU 使用率会减慢任务速度?

我在用耳语.cpp转录一些声音文件。这是一个占用大量 CPU 资源的进程,因此我尝试找到一些最佳设置,因此我对线程设置 (-t) 进行了一些测试,但结果非常令人困惑。这是我执行的命令:

date; time ./main -t [number of threads] -m ggml-model.bin -f 5min-16kHz.wav; date

我在配备 6 个内核(+ 6 个超线程内核)的 Intel i7 的 Macbook Pro 上运行此程序。

我尝试过默认设置(4 个线程)、6 和 12 线程(以及 14 线程,但没有产生任何输出,尽管所有 CPU 都以 100% 运行)。结果如下:

线程数 时间输出
4 1750.84s 用户 11.02s 系统 564% cpu 5:11.87 总计
4 1862.04s 用户 18.63s 系统 553% cpu 5:39.58 总计
6 2199.42s 用户 16.79s 系统 720% cpu 5:07.51 总计
6 2212.72s 用户 14.49s 系统 722% cpu 5:08.22 总计
12 4595.03s 用户 22.21s 系统 1053% cpu 7:18.47 总计
12 4298.11s 用户 22.53s 系统 1059% cpu 6:47.85 总计

正如您所看到的,随着线程数量的增加,CPU 负载也会增加。您可能期望实时时间随着 CPU 负载的增加而按比例减少(一分钟 100% 大约对应半分钟 200% 和两分钟 50%),但这里不会发生这种情况。

相反,我使用 4 个和 6 个线程获得了大致相同的实时结果,而运行 6 个线程时 CPU 使用时间增加了约 25%。 12 线程更糟糕,CPU 时间比 6 线程增加了一倍,实时时间增加了 40%。

我不明白这一点。当然,更多的线程不会线性扩展,但是执行相同任务时,CPU 时间应该保持相当恒定,与线程数量无关,不是吗?当 CPU 使用率增加时,实时时间应该减少吗?

考虑到任务和我的硬件,要使用的线程数的合理设置应该是多少?我期望它是核心数量+一点额外的数量,以防线程等待 I/O。我处理的声音文件是10 MB,whisper.cpp在32 GB的计算机上使用了约3,6 GB(目前大约10 GB未使用,内存压力为“绿色”)。


编辑:仅使用一个线程(-t 1)的对应值:

1619.90s 用户 20.86s 系统 197% cpu 13:48.78 总计

请注意,一个线程使用了几乎 200% 的 CPU。不确定我是否理解这一点。但 13 分钟的实时时间是有意义的。

编辑 2:添加更多 CPU (-p) 会使性能变差。

-t 6 -p 3- 6804.14s user 38.58s system 1040% cpu 10:57.84 total(实时时间的两倍,CPU 时间的 3-4 倍) -t 8 -p 2- 10573.58s user 57.47s system 1018% cpu 17:23.63 total(实时时间的 3 倍以上,CPU 时间的 6 倍) -t 4 -p 2- 2962.38s user 28.65s system 854% cpu 5:50.01 total(与 -t 4 大致相同)

我认为仅当您想限制此任务对计算机的影响程度时才应使用 -p 。否则,它只会使用尽可能多的处理器。

我不认为这是 I/O。它在前 5-10 秒内读取了 3.08 GB,然后在运行的其余部分(持续至少 5 分钟)读取了不到 10 MB。

编辑 3:使用-t 13,即比我的 CPU 支持的线程多一个,会产生非常奇怪的结果:93213.70s user 450.23s system 978% cpu 2:39:36.88 total不,我不是在开玩笑,CPU 时间是 的 50 倍以上-t 4,而 CPU 利用率几乎是其两倍(978 % vs 564 %),实时性提高了 30 倍以上。

如果与 CPU 时间增加 20 倍以上进行比较-t 12,CPU 使用率大致相同,并且实时性也增加了 20 倍以上。只需再添加一根线程即可。

这里有一些问题,不是吗?

编辑4:

选定的基准数据

./bench -m ./models/ggml-small.en.bin -t 4

system_info: n_threads = 4 / 12 | AVX = 1 | AVX2 = 1 | AVX512 = 0 | FMA = 1 | NEON = 0 | ARM_FMA = 0 | F16C = 1 | FP16_VA = 0 | WASM_SIMD = 0 | BLAS = 1 | SSE3 = 1 | VSX = 0 | 

whisper_print_timings:     load time =   540.82 ms
whisper_print_timings:   encode time =  3490.52 ms
whisper_print_timings:    total time =  4031.40 ms

5 个线程比 4 个线程快约 8%:

whisper_print_timings:     load time =   547.27 ms
whisper_print_timings:   encode time =  3193.27 ms
whisper_print_timings:    total time =  3740.58 ms

6 个线程比 5 个线程慢 1%:

whisper_print_timings:     load time =   591.16 ms
whisper_print_timings:   encode time =  3158.88 ms
whisper_print_timings:    total time =  3750.10 ms

7 个线程比 6 个线程慢 15%。从那里开始走下坡路。我猜这个任务只使用了我拥有的 6 个“真正的”核心,而不是超线程核心。我理论上认为 6 个线程应该比 5 个线程快,但我想计算机会执行一些其他任务,这些任务会中断其中一个线程并在运行此基准测试时不时使用一个核心。

编辑5:

使用 -20 的好值运行基准测试给出了一些有趣的结果(此处仅列出总时间)

Threads    Total time (ms)    ∆ (negative is better)
      4               3512    -13%
      5               3510     -6%
      6               3251 !! -13%
      7               3962     -8%

Δ 与具有正常优先级的相同数量的线程进行比较。具有高优先级的 6 个线程比具有普通优先级的默认设置快 19%。

答案1

在某些时候增加线程数量会限制计算内存,因此您将无法获得性能提升。 CPU 使用率不断增加,因为whisper.cpp我们使用繁忙循环来同步线程。这会占用更多的 CPU 资源,并浪费大量 CPU 周期,但有助于通过避免上下文切换和互斥体/条件变量的其他副作用来减少延迟。

--processors参数并不代表CPU处理器。这些是whisper.cpp并行处理音频的独立处理器。您可以在此处阅读有关此功能的更多信息:

https://github.com/ggerganov/whisper.cpp/pull/110

答案2

我已经用线程设置(-t)做了一些测试

这确实是一个非常好的主意......只要您还为 -p 选项指定一些内容:

-p N, --processors N [1 ] 计算期间使用的处理器数量

否则,使用的处理器数量将默认为 1。导致您的任务浪费大量时间进行上下文切换,而没有真正的利润。 (唯一的好处可能是利用一个线程等待 IO 的时间来获得另一个可运行线程的好处,但是,承认您的应用程序很大程度上受 CPU 限制......)

事实上,无论何种 CPU 密集型应用程序,在单个处理器工作的情况下,线程数量越多,浪费就越大……您刚刚……证明了这一点。 ;-)

因此我建议:

  • 1/确定基本性能,默认t=4; p=1
  • 2/ t=4 且 p=2 并增加 t 直至收益不显着,
  • 3/ 使用更高的 p 值重复 2,直到……您开始危及正在运行的其他应用程序。

请记住,我确信开发人员不会提供不明智的默认值,这些值会降低应用程序的性能。
在这里应用,开发人员似乎承认该应用程序相当受 IO 限制,因此建议 4 比 1 的 t/p 比率。


更新以下操作编辑

免责声明:我无法理解英特尔的 Turbo Boost 技术,特别是核心 i7 如何在内部决定修改核心频率/离线核心及其对多线程应用程序性能的影响。

编辑5:这组数据确实提供了逻辑数字:
降低 cpu 绑定线程的好值将增加在调度时允许保留 CPU 的时间,从而以显着降低成本的上下文切换来处理工作负载。
该组数据还表明仅使用了 6 个核心。是什么原因 ?涡轮增压 ? HT 禁用?如果你有办法准确地知道其中哪些......

相关内容