线程调整

用户线程在一个进程中提供独立的控制流。

如果用户线程需要访问内核服务(例如系统调用),相关的内核线程会为它服务。 用户线程在各种软件包中提供,最值得注意的是 pthreads 共享库 (libpthreads.a)。 通过 libpthreads 实现,用户线程位于虚拟处理器 (VP) 之上,而虚拟处理器本身位于内核线程之上。 一个多线程的用户进程可以使用两种模型之一,如下所述:
1:1 线程模型
1:1 模型是指每个用户线程正好拥有映射到它的一个内核线程。 此模型是所有 AIX® 版本上的缺省模型。 在这个模型里,每个用户线程都被绑定到一个 VP 上,并且正好与一个内核线程链接。 VP 不一定要绑定到一个实际的 CPU 上(除非已经绑定到一个处理器上)。 一个绑定到 VP 上的线程被称为具有系统作用域,因为它是和所有其他用户线程一起由内核调度程序直接调度的。
M:N 线程模型
AIX 4.3.1 中实现了 M:N 模型,现在也是默认模型。 在该模型中,几个用户线程可以共享同一个虚拟处理器或同一个 VP 池。 每个 VP 可以认为是一个可用的 CPU,可以执行用户代码和系统调用。 一个不绑定到 VP 上的线程被称为具有本地或进程作用域,因为它不和所有其他线程一起由内核调度程序直接调度。 pthreads 库会处理到 VP 的用户线程调度,然后内核会调度相关的内核线程。 从 AIX 4.3.2 开始,默认情况下一个内核线程映射到八个用户线程。 这是可以从应用程序内部或者通过一个环境变量调整的。

根据应用程序的类型,管理员可以选择使用不同的线程模型。 经过测试表明,某些应用程序使用 1:1 模型可能性能更高。 在 AIX 6.1中,缺省线程模型已从 M: N 更改回 1: 1。 对于所有 AIX 版本,只需为特定进程设置环境变量 AIXTHREAD_SCOPE=S ,即可将线程模型设置为 1: 1 ,然后在线程模型为 M:N 时将性能与其先前性能进行比较。

如果您看到一个应用程序在创建和删除线程,这可能是由于用户线程对内核线程的缺省比率是 8:1 而导致内核线程正在被收割 (harvested)。 这种收割连同库调度的开销一起可以影响性能。 另一方面,当存在几千个用户线程时,在库的用户空间里调度它们而不是管理几千个内核线程会使开销较少。 如果您在使用 pthread 时遇到性能问题,应该总是尝试改变作用域;在很多情况下,系统作用域可以提供更好的性能。

如果一个应用程序运行在 SMP 系统里,那么假如用户线程不能获取一个互斥对象,它会尝试自旋达 40 次。 互斥对象极可能只在短时间内可用,因此自旋较长时间可能是值得的。 当您添加更多 CPU 时,如果性能下降,这通常表示有锁定问题。 您可能想通过设置环境变量 SPINLOOPTIME=n 来增长自旋时间,其中 n 是自旋次数。 根据 CPU 速度和 CPU 数量来将值设置到几千这么高并不少见。 一旦自旋计数用尽,线程可以进入休眠状态以等待互斥对象变为可用,或者它可以发出 yield() 系统调用,并且只是放弃 CPU 但保持可执行状态(而不是进入休眠)。 缺省情况下,它会进入睡眠,但是通过将环境变量 YIELDLOOPTIME 设置为一个数,它会在进入睡眠之前放弃多达该数字那么多次。 每次它在放弃之后得到 CPU,它可以试图获取互斥对象。

大量使用 malloc 子系统的某些多线程用户进程可以通过在应用程序启动之前导出环境变量 MALLOCMULTIHEAP=1 来获得更好的性能。 对于多线程 C++ 程序来说,潜在性能改进的可能性特别大,因为这些程序在每次调用构造函数或者析构函数时都可以利用 malloc 子系统。 任何可用的性能改进在多线程用户进程运行在一个 SMP 系统上时最为明显,特别是使用系统作用域线程时(M:N 的比率为 1:1)。 然而,在有些情况下,改进在其他条件下和在单处理器上也可以很明显。