31

场景:我有一个示例应用程序,我有 3 种不同的系统配置 -

- 2 core processor, 2 GB RAM, 60 GB HHD,
- 4 core processor, 4 GB RAM, 80 GB HHD,
- 8 core processor, 8 GB RAM, 120 GB HHD

为了有效地利用我的应用程序的硬件功能,我希望配置 no。应用程序级别的线程数。但是,我希望只有在彻底了解系统功能后才能这样做。

是否有某种方法(系统/模式/工具)来参考最大和最小编号来确定系统能力。它可以提供最佳服务且不会降低效率和性能的线程数。通过这种方式,我可以只为我的应用程序配置那些将完全公正并为各自的硬件配置实现最佳性能的值。

已编辑1: 任何人都可以就如何为特定的硬件配置设置基线提供任何建议。

Edited2: 为了使其更直接 - 希望了解/了解我可以阅读的任何资源/文章,以便在一般/整体级别上对线程的 CPU 管理有所了解。

4

8 回答 8

69

要使用的最佳线程数取决于几个因素,但主要取决于可用处理器的数量以及任务的 CPU 密集程度。Java Concurrency in Practice提出了以下正式公式来估计最佳线程数:

N_threads = N_cpu * U_cpu * (1 + W / C)

在哪里:

  • N_threads 是最优线程数
  • N_cpu 是处理器的数量,您可以从Runtime.getRuntime().availableProcessors();
  • U_cpu 是目标 CPU 利用率(如果要使用全部可用资源,则为 1)
  • W / C 是等待时间与计算时间的比率(0 表示 CPU-bound 任务,可能是 10 或 100 表示慢 I/O 任务)

因此,例如,在 CPU 密集型场景中,您将拥有与 CPU 一样多的线程(有些人提倡使用该数字 + 1,但我从未见过它产生了显着差异)。

对于慢速 I/O 进程,例如网络爬虫,如果下载页面比处理页面慢 10 倍,则 W/C 可能为 10,在这种情况下,使用 100 个线程会很有用。

但请注意,在实践中存在上限(使用 10,000 个线程通常不会加快速度,并且您可能会在使用正常内存设置启动它们之前得到 OutOfMemoryError)。

如果您对应用程序运行的环境一无所知,这可能是您可以获得的最佳估计。在生产中分析您的应用程序可能使您能够微调设置。

虽然不是严格相关,但您可能还对Amdahl 定律感兴趣,该定律旨在衡量您可以从并行化程序中获得的最大加速。

于 2012-12-19T18:37:23.820 回答
15

我的建议是提供配置和命令行开关来分配每台机器的线程数。在用户/管理员没有明确配置应用程序的情况下,使用基于 Runtime.getRuntime().availableProcessors() 的启发式方法,如此处的其他答案所示。我强烈建议不要使用基于启发式的独家线程到核心猜测,原因如下:

  • 大多数现代硬件正朝着越来越模糊的“硬件线程”类型发展:英特尔的超线程和 AMD 的计算模块等 SMT 模型使公式变得复杂(详情如下),并且在运行时查询这些信息可能很困难。

  • 大多数现代硬件都具有涡轮增压功能,可根据活动核心和环境温度调整速度。随着涡轮增压技术的改进,速度范围 (ghz) 也在扩大。一些最新的 Intel 和 AMD 芯片的范围从 2.6ghz(所有内核活动)到 3.6ghz(单/双核活动),结合 SMT 可以意味着每个线程在以前的设计中获得 1.6ghz - 2.0ghz 的有效吞吐量。目前无法在运行时查询此信息。

  • 如果您不能有力地保证您的应用程序将是目标系统上运行的唯一进程,那么盲目消耗所有 cpu 资源可能不会取悦用户或服务器管理员(取决于软件是用户应用程序还是服务器应用程序) .

没有可靠的方法可以知道运行时机器的其余部分发生了什么,而不用您自己的家庭推出的多任务内核替换整个操作系统。您的软件可以通过查询进程和查看 CPU 负载等来尝试做出有根据的猜测,但这样做很复杂,并且有用性仅限于特定类型的应用程序(您的应用程序可能符合这些应用程序),并且通常受益于或需要提升或特权访问级别。

  • 现代病毒扫描程序现在通过设置现代操作系统提供的特殊优先级标志来工作,例如。他们让操作系统在“系统空闲”时告诉他们。操作系统的决定不仅仅基于 CPU 负载:它还考虑用户输入和可能由电影播放器​​设置的多媒体标志等。这对于大多数空闲任务来说很好,但对 CPU 密集型任务没有用处,例如你的。

  • 分布式家庭计算应用程序(BOINC、Folding@Home 等)通过定期查询正在运行的进程和系统 CPU 负载来工作——也许每秒或半秒一次。如果在不属于应用程序的进程上检测到连续多个查询的负载,则应用程序将暂停计算。一旦某些查询的负载变低,它就会恢复。需要多次查询,因为 CPU 负载读数因短暂的峰值而臭名昭著。仍然有一些注意事项: 1. 仍然鼓励用户手动重新配置 BOINC 以适应他们机器的规格。2.如果BOINC在没有Admin权限的情况下运行,那么它就不会知道其他用户启动的进程(包括一些服务进程),所以它可能会与那些不公平的CPU资源竞争。

关于 SMT(超线程,计算模块):

如今,大多数 SMT 将报告为硬件内核或线程,这通常不好,因为很少有应用程序在跨 SMT 系统上的每个内核扩展时表现最佳。更糟糕的是,查询一个核心是共享的 (SMT) 还是专用的通常无法产生预期的结果。在某些情况下,操作系统本身根本不知道(例如,Windows 7 不知道 AMD Bulldozer 的共享核心设计)。如果您可以获得可靠的 SMT 计数,那么经验法则是将每个 SMT 计为 CPU 密集型任务的半线程,以及大部分空闲任务的完整线程。但实际上,SMT 的权重取决于它执行的计算类型和目标架构。英特尔和 AMD 的 SMT 实现几乎完全相反,例如——英特尔' s 擅长并行运行加载整数和分支操作的任务。AMD 擅长并行运行 SIMD 和内存操作。

关于涡轮特性:

如今,大多数 CPU 都具有非常有效的内置 Turbo 支持,这进一步降低了跨系统所有内核扩展所获得的价值。更糟糕的是,turbo 功能有时会基于系统的实际温度和 CPU 负载一样多,因此冷却塔本身的冷却系统会像 CPU 规格一样影响速度。例如,在特定的 AMD A10(推土机)上,我观察到它在两个线程上以 3.7ghz 的速度运行。当第三个线程启动时,它下降到 3.5ghz,当第四个线程启动时,它下降到 3.4ghz。由于它也是集成GPU,所以当四个线程加上GPU工作时,它一直下降到大约3.0ghz(A10 CPU内部在高负载情况下优先考虑GPU);但仍然可以在 2 个线程和 GPU 处于活动状态时召集 3.6ghz。由于我的应用程序同时使用了 CPU 和 GPU,这是一个重要的发现。我能够通过将进程限制为两个 CPU 绑定线程来提高整体性能(另外两个共享内核仍然有用,它们充当 GPU 服务线程——能够唤醒并快速响应以将新数据推送到 GPU,如所须)。

...但与此同时,我在 4x 线程上的应用程序在安装了更高质量冷却设备的系统上可能表现得更好。这一切都非常复杂。

结论:没有好的答案,而且由于 CPU SMT/Turbo 设计领域不断发展,我怀疑很快就会有好的答案。您今天制定的任何体面的启发式方法明天都可能不会产生理想的结果。所以我的建议是:不要在这上面浪费太多时间。根据核心数量粗略猜测一些足够适合您本地目的的东西,允许它被 config/switch 覆盖,然后继续。

于 2012-12-24T18:29:14.070 回答
14

您可以像这样获取 JVM 可用的处理器数量:

Runtime.getRuntime().availableProcessors()

然而,不幸的是,从可用处理器的数量计算最佳线程数并非易事。这在很大程度上取决于应用程序的特性,例如,受 CPU 限制的应用程序具有比处理器数量更多的线程几乎没有意义,而如果应用程序主要受 IO 限制,您可能希望使用更多线程。您还需要考虑系统上是否正在运行其他资源密集型进程。

我认为最好的策略是根据经验为每个硬件配置确定最佳线程数,然后在您的应用程序中使用这些数字。

于 2012-12-12T07:56:30.997 回答
4

我同意此处推荐最佳猜测方法的其他答案,并提供覆盖默认值的配置。

此外,如果您的应用程序特别占用 CPU,您可能需要考虑将您的应用程序“固定”到特定的处理器上。

你没有说你的主要操作系统是什么,或者你是否支持多个操作系统,但大多数都有一些方法可以做到这一点。例如,Linux 有任务集

一种常见的方法是避免 CPU 0(操作系统始终使用),并将应用程序的 cpu 亲和性设置为同一套接字中的一组 CPU。

让应用程序的线程远离 cpu 0(如果可能,远离其他应用程序)通常可以通过减少任务切换量来提高性能。

将应用程序保持在一个套接字上可以通过减少应用程序线程在 cpu 之间切换时的缓存失效来进一步提高性能。

与其他一切一样,这高度依赖于您正在运行的机器的架构,以及正在运行的其他应用程序。

于 2012-12-25T01:26:00.320 回答
2

使用VisualVm工具监控线程。首先在程序中创建最小线程并查看其性能。然后增加程序内的线程数并再次分析其性能。希望对您有所帮助。

于 2012-12-21T13:00:40.773 回答
1

我在这里使用这个 Python 脚本来确定内核(和内存等)的数量,以启动具有最佳参数和人体工程学的 Java 应用程序。Github 上的 PlatformWise

它的工作原理是这样的:编写一个 python 脚本,调用getNumberOfCPUCores()上述脚本中的 来获取内核数量,并getSystemMemoryInMB()获取 RAM。您可以通过命令行参数将该通知传递给您的程序。然后,您的程序可以根据内核数量使用适当数量的线程。

于 2012-12-25T06:39:43.443 回答
1

在应用程序级别创建线程是好的,并且在多核处理器中,在内核上执行单独的线程以提高性能。因此,为了利用内核处理能力,实现线程是最佳实践。

我的想法:

  1. 一次只有 1 个程序线程将在 1 个内核上执行。
  2. 具有 2 个线程的相同应用程序将在 2 个内核上执行一半时间。
  3. 具有 4 个线程的相同应用程序将在 4 个核心上执行得更快。

因此,您开发的应用程序应该具有线程级别<= 内核数。

线程执行时间由操作系统管理,是一种高度不可预测的活动。CPU 执行时间称为时间片或量子。如果我们创建越来越多的线程,操作系统会花费这个时间片中的一小部分来决定哪个线程先执行,从而减少每个线程获得的实际执行时间。换句话说,如果有大量线程排队,每个线程将做更少的工作。

阅读本文以了解如何实际利用 cpu 核心的精彩内容。 csharp-codesamples.com/2009/03/threading-on-multi-core-cpus/

于 2012-12-26T09:17:27.990 回答
1

然而,不幸的是,从可用处理器的数量计算最佳线程数并非易事。这在很大程度上取决于应用程序的特性,例如,受 CPU 限制的应用程序具有比处理器数量更多的线程几乎没有意义,而如果应用程序主要受 IO 限制,您可能希望使用更多线程。您还需要考虑系统上是否正在运行其他资源密集型进程。

于 2017-09-06T04:25:39.130 回答