增加线程和进程优先级以减少处理器密集型并行应用程序的执行时间

5
我知道在线社区上设置线程优先级有些禁忌,但我相信我的应用程序是一个适合提高优先级的好候选者。为了证明这一点,我已经在下面解释了背景情况。现在的问题是如何有效地做到这一点?
该应用程序是.NET 4.0(C#)控制台应用程序,执行一个大约五个小时的复杂算法。该算法不会占用太多内存,只占用处理器资源。它进行数字计算,不执行任何磁盘I/O、数据库连接、网络连接等操作。该应用程序的输出仅是一个数字,它在结束时将其写入控制台。换句话说,该算法是完全独立的,没有任何依赖关系。
该应用程序在其自己的专用16核64位机器上运行,运行Windows Server,比它所需的空闲RAM要多得多(8GB)。专用意味着该服务器已被购买,专门用于运行此应用程序。
我已经尽可能通过广泛的分析、花式数学技巧和位操作技巧来优化代码。
以下是伪代码的总体结构:
public static void Main ()
{
    Process.GetCurrentProcess().PriorityBoostEnabled = true;
    Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.RealTime;

    // Of course this only affects the main thread rather than child threads.
    Thread.CurrentThread.Priority = ThreadPriority.Highest;

    BigInteger seed = SomeExtremelyLargeNumber; // Millions of digits.

    // The following loop takes [seed] and processes some numbers.
    result1 = Parallel.For(/* With thread-static variables. */);

    while (true) // Main loop that cannot be parallelized.
    {
        // Processes result1.
        result2 = Parallel.For(/* With thread-static variables. */);

        // Processes result2.
        result1 = Parallel.For(/* With thread-static variables. */);

        if (result1 == criteria)
            break;

        // Note: This loop does not need to sleep or care about system responsiveness.
    }
}

根据SO上与线程优先级相关的问题,我了解到任何使用ThreadPool的内容都不应该在优先级方面进行更改。所以如果需要切换到手动线程,那就这么做。

问题:

  • 如何将上述代码更改为手动线程以从增加的线程优先级中受益(不使用线程池等)?
  • 将所有子线程的优先级设置为最高是否有帮助?我的意思是,子线程是否会相互争夺,还是会使它们在外部OS任务中具有优势?
  • 考虑到有16个核心,我应该运行16个或15个线程?是否有一般的指导方针?
  • 将进程优先级设置为实时是否也有帮助?

作为我的兴趣之一,我有一个位操作库,很快就会在SourceForge上发布。如果您需要,我可以通过电子邮件发送uint或ulong的例程给您。 - IvoTops
我知道现在已经过去了5年,但如果你仍在继续这个项目,你可能想要研究一下C++ AMP来利用GPU的处理能力。GPU非常擅长并行工作。虽然学习曲线有点陡峭,但如果做得正确,它可能会执行代码更快。你也可以阅读一下CUDA和OpenCL的相关资料。 - johnildergleidisson
4个回答

2

使用这样的应用程序,我希望更改优先级对总运行时间没有任何影响。如果您已经使用所有16个核心进行实际工作并且CPU利用率已经达到100%,则无法做更多事情。


我猜将进程级别的优先级设置为实时应该就能解决所有问题了,对吗?顺便问一下,你的意思是我应该继续使用TPL还是切换到手动线程? - Raheel Khan
2
只有当线程池表现不如预期时,我才会更改您的设计。如果任务管理器显示所有内核均为100%,那就是这样。将进程优先级更改为实时也不会产生太大差异,但是,如果您可以足够提高进程/线程的优先级,则可能完全使机器死机,直到应用程序完成,即防止任务管理器获得任何CPU。降低进程优先级也不会有任何影响。试试看! - Martin James
听起来很合理。令人难过的是,在专用场景中,没有什么能显著减少执行时间。 - Raheel Khan
如果你正在使用网页浏览器、M$ Office Word等应用程序,它们对整体运行时间的影响也不大。这些其他应用程序将使用可用CPU的0.1%,而你的应用程序将占据其余的99.9%。 - Martin James
我完全同意。线程和进程的优先级更多地涉及时间限制而非负载优化。 - Brannon

1
我的代码可以更改进程和线程的优先级。
public void SetPriorityProcessAndTheards(string nameProcess,ProcessPriorityClass processPriority, ThreadPriorityLevel threadPriorityLevel)
{
    foreach(Process a in Process.GetProcessesByName(nameProcess))
    {
        a.PriorityBoostEnabled = true;
        a.PriorityClass = processPriority;

        foreach(ProcessThread processThread in a.Threads)
        {
            processThread.PriorityLevel = threadPriorityLevel;
            processThread.PriorityBoostEnabled = true;
        }
    }
}

1

你不需要为单个线程设置优先级,只需要为整个进程设置优先级,因为大多数线程显然都在执行重要的工作。

但是,对于像你的 CPU 密集型应用程序这样的应用程序,我不认为它会有任何影响。唯一能强制抢占您自己进程的进程是 I/O 密集型应用程序,这些应用程序传统上受到大多数操作系统的青睐,但由于你有专用机器,这不会成为问题(此外,根据我的经验,Windows Server 相当轻量级,因此如果你的应用程序是唯一运行的应用程序,它不会干扰你)。

顺便说一下:

该算法根本不占用内存,只占用处理器。它进行数字处理,并且不执行任何磁盘 I/O、数据库连接、网络连接等操作。

它不执行“明显”的 I/O 操作并不意味着它不能占用内存。如果正在处理大型数组或其他数据结构,则 CPU 将不断向主内存发出读/写操作,并且需要将数据移动到各种内存级别之间,处理仅仅数字也可能会负面影响程序的性能,所以即使只是处理数字,也必须正确使用。


根据我的逻辑,内存中最多会有200个BigInteger对象,占用不超过100MB。还有其他的对象漂浮在周围。 - Raheel Khan
@Raheel Khan:这只是我所做的一项观察。我不知道它是否适用于你的代码,但它可能会在一般情况下发生。 - Tudor

0

我希望你将线程数限制在核心/线程数的范围内。有时并行任务库会使用过多的线程。对于您的 CPU 最大化进程,核心计数或线程计数(添加了超线程虚拟核心)是最好的选择,因此请提供并修复线程计数;

 // Create a ParallelOptions object and supply this to the Parallel.For() 

 var po = new ParallelOptions {MaxDegreeOfParallelism = Environment.ProcessorCount}
 Parallel.For(,,po,);

 // Environment.ProcessorCount gives number of Cores (NOT processors)
 // Never found out how to detect fake cores or hyperthreads, check Task Monitor ;-)

您可以在所有parallel.For()语句中重复使用po对象。即使在CPU绑定的线程应用程序上,我也从未真正受益于优先级调整。


我已经使用Environment.ProcessorCount来让所有核心工作。我还没有想出如何获取虚假核心,但我怀疑每个虚假核心运行1个线程将不会有益处。实际上可能会使情况更糟。 - Raheel Khan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接