我的建议是提供配置和命令行开关,用于分配每台计算机的线程数。使用基于 Runtime.getRuntime().availableProcessors() 的启发式方法,如此处的其他答案所示,以防用户/管理员未以不同方式显式配置应用程序。我强烈建议不要使用基于启发式的独占线程到内核猜测,原因如下:
大多数现代硬件正朝着越来越模糊的“硬件线程”类型发展:SMT模型(如英特尔的Hyperthreading和AMD的计算模块)使公式复杂化(详见下文),并且在运行时查询此信息可能很困难。
大多数现代硬件都具有turbo功能,可根据活动内核和环境温度来扩展速度。随着涡轮增压技术的进步,速度范围(ghz)也在增长。最近的一些英特尔和AMD芯片的范围可以从2.6ghz(所有内核处于活动状态)到3.6ghz(单/双核活动),这与SMT相结合,可能意味着每个线程在前一种设计中获得有效的1.6ghz - 2.0ghz吞吐量。当前无法在运行时查询此信息。
如果您没有强有力的保证您的应用程序将是目标系统上运行的唯一进程,那么盲目地消耗所有CPU资源可能不会让用户或服务器管理员满意(取决于软件是用户应用程序还是服务器应用程序)。
没有可靠的方法可以知道在运行时机器的其余部分发生了什么,除非用你自己的家庭滚动的多任务内核替换整个操作系统。您的软件可以通过查询进程和查看 CPU 负载等来尝试进行有根据的猜测,但这样做很复杂,并且有用性仅限于特定类型的应用程序(您的应用程序可能符合条件),并且通常受益于或需要提升或特权访问级别。
现代病毒扫描程序现在通过设置现代操作系统提供的特殊优先级标志来工作,例如。他们让操作系统告诉他们“系统空闲”的时间。操作系统的决策不仅仅是基于CPU负载:它还考虑可能由电影播放器等设置的用户输入和多媒体标志。这对于大多数空闲任务来说很好,但对于像您这样的CPU密集型任务没有用处。
分布式家庭计算应用程序(BOINC,Folding@Home等)通过定期查询正在运行的进程和系统CPU负载来工作 - 也许每隔一秒或半秒一次。如果在不属于应用的进程上检测到负载,则连续多个查询,则应用将暂停计算。一旦某些数量的查询的负载变低,它就会恢复。需要多个查询,因为 CPU 负载读数因短暂的峰值而臭名昭著。仍然有一些警告:1.仍然鼓励用户手动重新配置BOINC以适应其机器的规格。2.如果BOINC在没有管理员权限的情况下运行,那么它将不会知道其他用户(包括某些服务进程)启动的进程,因此它可能会不公平地与CPU资源竞争。
关于SMT(超线程,计算模块):
如今,大多数SMT将报告为硬件内核或线程,这通常不好,因为很少有应用程序在SMT系统上的每个内核上扩展时都能以最佳状态运行。更糟糕的是,查询内核是共享的(SMT)还是专用的通常无法产生预期的结果。在某些情况下,操作系统本身根本不知道(例如,Windows 7不知道AMD Bulldozer的共享核心设计)。如果可以获得可靠的 SMT 计数,则经验法则是将每个 SMT 计数为 CPU 密集型任务的半线程,以及大多数空闲任务的全线程。但实际上,SMT的权重取决于它所做的计算类型以及目标架构。例如,英特尔和AMD的SMT实现几乎相反 - 英特尔擅长并行运行加载整数和分支操作的任务。AMD 擅长并行运行 SIMD 和内存操作。
关于涡轮增压器功能:
如今,大多数CPU都具有非常有效的内置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设计领域不断发展,我怀疑很快就会有一个好的答案。你今天制定的任何像样的启发式方法很可能明天都不会产生理想的结果。所以我的建议是:不要浪费太多时间。根据核心数量粗略猜测一些东西,这些核心数量非常适合您的本地目的,允许它被配置/开关覆盖,然后继续前进。