gpt4 book ai didi

c# - Parallel.For 循环的完整 CPU 使用率

转载 作者:行者123 更新时间:2023-11-30 20:14:56 25 4
gpt4 key购买 nike

我正在编写一个 WPF 应用程序来处理来自红外摄像机的图像数据流。该应用程序使用一个类库来处理重新缩放或着色等步骤,这也是我自己编写的。图像处理步骤看起来像这样:

ProcessFrame(double[,] frame)
{
int width = frame.GetLength(1);
int height = frame.GetLength(0);
byte[,] result = new byte[height, width];
Parallel.For(0, height, row =>
{
for(var col = 0; col < width; ++col)
ManipulatePixel(frame[row, col]);
});
}

帧由在后台运行的任务处理。问题是,根据特定处理算法 ( ManipulatePixel() ) 的成本,应用程序无法再跟上相机的帧速率。但是,我注意到尽管我使用了并行 for 循环,但应用程序根本不会使用所有可用的 CPU - 任务管理器性能选项卡显示大约 60-80% 的 CPU 使用率。

我之前在 C++ 中使用过相同的处理算法,使用并行模式库中的 concurrency::parallel_for 循环。正如我所料,C++ 代码使用了它可以获得的所有 CPU,而且我还尝试了 PInvoking 我的 C# 代码中的 C++ DLL,执行在 C# 库中运行缓慢的相同算法 - 它还使用了所有可用的 CPU 能力,CPU 使用率几乎始终保持在 100%,并且跟上相机的速度完全没有问题。

将代码外包到 C++ DLL 中,然后将其编码回 C# 是一个额外的麻烦,我当然宁愿避免。如何让我的 C# 代码真正利用所有 CPU 潜力?我试过像这样增加进程优先级:

  using (Process process = Process.GetCurrentProcess())
process.PriorityClass = ProcessPriorityClass.RealTime;

有影响,但影响很小。我还尝试为 Parallel.For() 循环设置并行度,如下所示:

ParallelOptions parallelOptions = new ParallelOptions();
parallelOptions.MaxDegreeOfParallelism = Environment.ProcessorCount;

然后将其传递给 Parallel.For() 循环,这根本没有效果,但我想这并不奇怪,因为默认设置应该已经优化过了。我还尝试在应用程序配置中进行设置:

<runtime>
<Thread_UseAllCpuGroups enabled="true"></Thread_UseAllCpuGroups>
<GCCpuGroup enabled="true"></GCCpuGroup>
<gcServer enabled="true"></gcServer>
</runtime>

但这实际上使它运行得更慢。


编辑:我最初引用的 ProcessFrame 代码块实际上并不完全正确。我当时在做的是:

ProcessFrame(double[,] frame)
{
byte[,] result = new byte[frame.GetLength(0), frame.GetLength(1)];
Parallel.For(0, frame.GetLength(0), row =>
{
for(var col = 0; col < frame.GetLength(1); ++col)
ManipulatePixel(frame[row, col]);
});
}

对此感到抱歉,当时我正在解释代码,但我没有意识到这是一个实际的陷阱,会产生不同的结果。从那以后,我将代码更改为我最初编写的代码(即在函数开头设置的宽度和高度变量,并且数组的长度属性每个只查询一次,而不是在 for 循环的条件语句中查询)。谢谢@Seabizkit,你的第二条评论启发了我尝试这个。事实上,这种变化已经使代码运行速度明显加快——我没有意识到这一点,因为 C++ 不知道二维数组,所以无论如何我不得不将像素尺寸作为单独的参数传递。但是我还不能说它是否足够快。

也感谢您提供的其他答案,它们包含很多我还不知道的东西,但很高兴知道我必须寻找什么。一旦我达到满意的结果,我会更新。

最佳答案

我需要拥有您的所有代码并能够在本地运行它才能诊断问题,因为您的帖子没有详细信息(我需要查看您的 ManipulatePixel 函数内部,以及调用 ProcessFrame 的代码)。但这里有一些适用于您的情况的一般提示。

  • .NET 中的二维数组比一维数组和交错数组慢得多,即使在今天的 .NET Core 中也是如此 - 这是一个长期存在的错误。

  • 在线程之间共享内存缓冲区使系统更难优化安全内存访问。

  • 避免为遇到的每个帧分配新的缓冲区 - 如果帧的生命周期有限,则考虑使用缓冲池使用可重复使用的缓冲区。
  • 考虑使用 .NET 中的 SIMD 和 AVX 功能。虽然现代 C/C++ 编译器足够智能,可以编译代码以使用这些指令,但 .NET JIT 并不是那么热门 - 但您可以显式调用 SMID/AVX instructions using the SIMD-enabled types (您需要使用 .NET Core 2.0 或更高版本以获得最佳加速功能)

  • 此外,避免在 C# 中的 for 循环内复制单个字节或标量值,而是考虑使用 Buffer.BlockCopy 进行批量复制操作(因为这些可以使用硬件内存复制功能)。

  • 关于您对“80% CPU 使用率”的观察 - 如果您在程序中有一个循环,那么在操作系统提供的时间片内导致 100% CPU 使用率-系统 - 如果您没有看到 100% 使用率,那么您的代码:

    • 您的代码实际上比实时运行更快(这是一件好事!)-(除非您确定您的程序跟不上输入?)<
    • 您的代码线程(或多个线程)被某些东西阻塞,例如阻塞 IO 调用或放错位置的 Thread.Sleep。当您认为应该受 CPU 限制时,使用 ETW 等工具查看您的进程正在做什么。
    • 确保您没有使用任何lock(Monitor)调用或使用其他线程或内存同步原语。

关于c# - Parallel.For 循环的完整 CPU 使用率,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58026420/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com