Art*_*tur 7 php cpu operating-system scheduling fpm
假设我有一个 4 核和 4 线程的 CPU,通过设置选项运行例如 8 个 PHP-FPM 工作程序是否有意义pm.max_children = 8?就我而言,具有 4 个线程的 CPU 最多只能“真正”并行运行 4 个进程。如果由于这 8 个进程之间的上下文切换而导致 CPU 时间损失,难道不会造成开销吗?
相比之下,Node.js 集群模式文档建议运行与核心数量一样多的工作进程/子进程。同样的建议不也适用于此吗?
Ber*_*ner 13
是的,这是有道理的,你应该总是这样做,让我解释一下原因。
PHP 不使用线程并在单核上运行。PHP-FPM 会产生许多工作线程,因此您可以在多个核心上运行进程。
了解操作系统如何使用进程上下文切换来同时处理多个进程非常重要。如果您只有一个核心,您仍然可以同时在计算机上运行多个进程,其原因是进程上下文切换。这意味着操作系统将使用单核并在进程之间动态切换,根据各种因素一次处理每个进程一点,例如进程是否正在等待某些 I/O、进程运行了多长时间、重要的是进程上下文切换需要一些时间,并且单核在多个进程之间共享。
如果您有多个核心,则可以在每个核心上并行执行进程,但是很可能您运行的进程仍然多于核心,因此进程上下文切换仍然会发生,只是速度较低。
之所以建议将其设置pm.max_children为高于CPU核心的值,是因为在大多数情况下,您的php进程并不执行密集的CPU任务,而是主要等待I/O,例如等待SQL结果,等待某些curl 响应或某些磁盘读写响应。这些操作称为 I/O 阻塞,通常是请求中消耗大部分时间的操作。通过将 设定pm.max_children为比核心数更高的值(有时甚至是核心数量的 5-10 倍),您可以从操作系统在进程处于阻塞/空闲状态时执行的上下文切换中受益。
很可能有超过 20 个 PHP 进程正在运行只是等待 IO。如果您将其设置pm.max_children为核心数量(假设为 8 个),则核心可能不会执行太多操作,并且大量请求会堆积起来,响应速度会非常慢。
例如,如果您确定您的 php 进程没有阻塞 I/O 并且仅执行一些计算,那么您实际上可能会从仅设置与核心相同数量的 pm.max_children 中获益更多,原因是进程上下文切换会减慢速度关闭并且运行更多进程会消耗更多资源。然而,这种情况并不常见,您的进程很可能确实存在 I/O 阻塞和空闲时间。
这里有一篇深入探讨 Linux 上的进程上下文切换的好文章。
swoole PHP 扩展中还使用了一种称为协程的东西。协程还使用上下文切换来执行并发,但是这是以编程方式完成的,比操作系统上下文切换消耗更少的资源并且速度更快。如果使用 swoole,则不需要 php-fpm,因为它更快,但它还有其他您需要关心的问题。然而,对于 swoole,建议您设置与核心一样多的工作进程,以避免操作系统上下文切换。您可以拥有数千个协程,而不会影响性能太多。
Nodejs 使用类似于 swoole 协程的事件循环。建议将工作线程设置为与您的核心相匹配的原因是为了避免操作系统上下文切换并使用内置的上下文切换,因为它更快、更轻。
一般答案是肯定的,因为尽管您不能并行运行那么多线程,但您可以同时运行它们。
需要理解的关键一点是,在大多数实际应用程序中,处理请求的大量时间并不是使用本地 CPU,而是用于等待数据库查询、外部 API,甚至磁盘访问。如果每个 CPU 核心有一个线程,那么 CPU 就一直处于空闲状态。允许额外的线程,一个线程可以使用 CPU,而另一个线程则等待外部数据。
只有当您的应用程序非常不寻常并且 100% 的时间都在使用 CPU 时,限制每个核心一个线程才有意义。
这不适用于 Node.js 的原因是它使用异步代码在单个线程内实现并发:您可以告诉当前线程“开始执行此操作,在等待结果的同时,继续处理不同的请求” 。这对于原生 PHP 来说是不可能的,它使用“无共享”方法 - 每个请求都有自己的线程或进程 - 但有一些项目(例如 Swoole 和 Amp)添加了对这种异步方法的支持。