任务并行不稳定,有时使用 100% CPU

2024-04-23

我目前正在测试 C# 的 Parallel。一般来说,它工作得很好,并且使用并行比普通的 foreach 循环更快。然而,有时(比如五分之一),我的 CPU 会达到 100% 使用率,导致并行任务非常慢。我的 CPU 设置是 i5-4570 和 8GB 内存。有谁知道为什么会出现这个问题?

以下是我用来测试该功能的代码

            // Using normal foreach
            ConcurrentBag<int> resultData = new ConcurrentBag<int>();
            Stopwatch sw = new Stopwatch();
            sw.Start();
            foreach (var item in testData)
            {
                if (item.Equals(1))
                {
                    resultData.Add(item);
                }
            }
            Console.WriteLine("Normal ForEach " + sw.ElapsedMilliseconds);

            // Using list parallel for
            resultData = new ConcurrentBag<int>();
            sw.Restart();
            System.Threading.Tasks.Parallel.For(0, testData.Count() - 1, (i, loopState) =>
            {
                int data = testData[i];
                if (data.Equals(1))
                {
                    resultData.Add(data);
                }
            });
            Console.WriteLine("List Parallel For " + sw.ElapsedMilliseconds);

            // Using list parallel foreach
            //resultData.Clear();
            resultData = new ConcurrentBag<int>();
            sw.Restart();
            System.Threading.Tasks.Parallel.ForEach(testData, (item, loopState) =>
            {
                if (item.Equals(1))
                {
                    resultData.Add(item);
                }
            });
            Console.WriteLine("List Parallel ForEach " + sw.ElapsedMilliseconds);

            // Using concurrent parallel for 
            ConcurrentStack<int> resultData2 = new ConcurrentStack<int>();
            sw.Restart();
            System.Threading.Tasks.Parallel.For(0, testData.Count() - 1, (i, loopState) =>
            {
                int data = testData[i];
                if (data.Equals(1))
                {
                    resultData2.Push(data);
                }
            });
            Console.WriteLine("Concurrent Parallel For " + sw.ElapsedMilliseconds);

            // Using concurrent parallel foreach
            resultData2.Clear();
            sw.Restart();
            System.Threading.Tasks.Parallel.ForEach(testData, (item, loopState) =>
            {
                if (item.Equals(1))
                {
                    resultData2.Push(item);
                }
            });
            Console.WriteLine("Concurrent Parallel ForEach " + sw.ElapsedMilliseconds);

正常输出

第493章

315 的并行列表

列出并行 ForEach 328

并发并行 286

并发并行 ForEach 292

CPU 使用率 100% 期间

第476章 正常

8047 的并行列表

列出并行 ForEach 276

并发并行 281

并发并行 ForEach 3960

(这可能发生在任何并行任务期间,以上只是一个实例)

Update

通过使用@willaien提供的PLINQ方法并运行100次,这个问题不再出现。我仍然不知道为什么这个问题首先会出现。

var resultData3 = testData.AsParallel().Where(x => x == 1).ToList();

首先,要小心Parallel- 它不能保护您免受线程安全问题的影响。在原始代码中,您在填充结果列表时使用了非线程安全代码。一般来说,您希望避免共享任何状态(尽管在这种情况下对列表的只读访问是可以的)。如果你真的想使用Parallel.For or Parallel.ForEach用于过滤和聚合(实际上,AsParallel是您在这些情况下想要的),您应该使用线程本地状态的重载 - 您将在localFinally委托(请注意,它仍然在不同的线程上运行,因此您需要确保线程安全;但是,在这种情况下,锁定就可以了,因为您只为每个线程执行一次,而不是在每次迭代时执行此操作)。

现在,解决此类问题显然首先要尝试的是使用探查器。所以我就这么做了。结果如下:

  • 这些解决方案中几乎没有任何内存分配。与最初的测试数据分配相比,它们完全相形见绌,即使是相对较小的测试数据(我在测试时使用了 1M、10M 和 100M 的整数)。
  • 正在完成的工作是在例如Parallel.For or Parallel.ForEach主体本身,而不是在您的代码中(简单的if (data[i] == 1) results.Add(data[i])).

第一个意味着我们可以说 GC 可能不是罪魁祸首。确实,它没有任何逃跑的机会。第二个更奇怪 - 这意味着在某些情况下,Parallel太不合规矩了——但它看起来是随机的,有时它可以顺利工作,有时需要半秒钟。这通常会指向 GC,但我们已经排除了这种可能性。

我尝试过在没有循环状态的情况下使用重载,但这没有帮助。我尝试过限制MaxDegreeOfParallelism,但它只会伤害事物。现在,显然,这段代码绝对由缓存访问主导——几乎没有任何 CPU 工作,也没有 I/O——这总是有利于单线程解决方案;但即使使用MaxDegreeOfParallelism1 没有帮助 - 事实上,2 似乎是我的系统上最快的。更多是没有用的——同样,缓存访问占主导地位。它仍然很好奇 - 我使用服务器 CPU 进行测试,它为所有数据一次提供足够的缓存,虽然我们没有进行 100% 顺序访问(这几乎完全消除了延迟) ),它应该足够连续。无论如何,我们在单线程解决方案中拥有内存吞吐量的基线,并且当它运行良好时,它非常接近并行情况的速度(并行,我读到运行时间比单线程少 40%,在四核服务器 CPU 来解决令人尴尬的并行问题 - 显然,内存访问是限制)。

所以,是时候检查一下参考来源了Parallel.For。在这种情况下,它只是根据工作人员的数量创建范围 - 每个范围。所以这不是范围——没有任何开销。 核心只是运行一个在给定范围内迭代的任务。有一些有趣的地方 - 例如,如果任务花费太长时间,任务将被“暂停”。然而,它似乎不太适合数据 - 为什么这样的事情会导致与数据大小无关的随机延迟?无论工作多么小,无论多么低MaxDegreeOfParallelism也就是说,我们得到了“随机”的减速。这可能是一个问题,但我不知道如何检查它。

最有趣的是,扩展测试数据对异常没有任何作用 - 虽然它使“好”并行运行得更快(甚至在我的测试中接近完美效率,奇怪的是),“坏”并行运行仍然只是一样糟糕。事实上,在我的一些测试中,它们是absurdly不好(最多是“正常”循环的十倍)。

那么,让我们看一下线程。我人为地增加了线程的数量ThreadPool确保扩展线程池不是瓶颈(如果一切正常,则不应成为瓶颈,但是......)。第一个惊喜来了 - 虽然“好的”运行只使用有意义的 4-8 个线程,但“坏的”运行会扩展到池中的所有可用线程,即使有一百个线程。哎呀?

让我们再次深入研究源代码。Parallel内部使用Task.RunSynchronously运行根分区工作作业,以及Wait就结果而言。当我查看并行堆栈时,有 97 个线程在执行循环体,而只有一个线程实际上执行了循环体。RunSynchronously在堆栈上(正如预期的那样 - 这是主线程)。其他是普通的线程池线程。任务 ID 也讲述了一个故事 - 有数千进行迭代时创建的各个任务的数量。显然,有些东西是very这里错了。即使我删除整个循环体,这种情况仍然会发生,所以这也不是什么奇怪的闭包。

显式设置MaxDegreeOfParallelism在某种程度上抵消了这一点——使用的线程数量不再激增——但是,任务数量仍然如此。但我们已经看到范围只是运行的并行任务的数量 - 那么为什么还要继续创建越来越多的任务呢?使用调试器证实了这一点 - 当 MaxDOP 为 4 时,只有五个范围(有一些对齐导致第五个范围)。有趣的是,其中一个已完成的范围(第一个范围如何比其他范围领先这么多?)有索引higher比它迭代的范围 - 这是因为“调度程序”在最多 16 个切片中分配范围分区。

根任务是自我复制的,因此不必显式启动,例如四个任务来处理数据,它等待调度程序复制任务来处理更多数据。这有点难以阅读 - 我们正在谈论复杂的多线程无锁代码,但似乎它always将工作分配到比分区范围小得多的切片中。在我的测试中,切片的最大大小为 16 - 与我正在运行的数百万数据相差甚远。像这样的主体进行 16 次迭代根本没有时间,这可能会导致算法出现许多问题(最大的问题是基础设施比实际迭代器主体占用更多的 CPU 工作)。在某些情况下,缓存垃圾可能会进一步影响性能(也许当主体运行时有很多变化时),但大多数时候,访问是足够顺序的。

TL; DR

不要使用Parallel.For and Parallel.ForEach如果您的每次迭代工作非常短(大约毫秒)。AsParallel或者仅以单线程运行迭代很可能会快得多。

稍微长一点的解释:

看起来Parallel.For and Paraller.ForEach专为您迭代的单个项目需要大量时间来执行的场景而设计(即每个项目有大量工作,而不是大量项目的少量工作)。当迭代器主体太短时,它们似乎表现不佳。如果您没有在迭代器主体中进行大量工作,请使用AsParallel代替Parallel.*。最佳点似乎是每个切片 150 毫秒以下(每次迭代大约 10 毫秒)。否则,Parallel.*会在自己的代码中花费大量时间,而几乎没有时间进行迭代(在我的例子中,通常的数字在主体中大约占 5-10% - 糟糕得令人尴尬)。

遗憾的是,我在 MSDN 上没有找到任何关于此问题的警告 - 甚至有样本检查了大量数据,但没有任何迹象表明这样做会对性能造成严重影响。在我的计算机上测试完全相同的示例代码,我发现它确实通常比单线程迭代慢,而且在最好的情况下,也几乎快不了多少(大约节省 30-40% 的时间)在四个 CPU 核心上运行时- 效率不高)。

EDIT:

Willaien 在 MSDN 上发现了关于这个问题的提及,以及如何解决它 -https://msdn.microsoft.com/en-us/library/dd560853(v=vs.110).aspx https://msdn.microsoft.com/en-us/library/dd560853(v=vs.110).aspx。这个想法是使用自定义分区器并在Parallel.For主体(例如循环Parallel.For的循环)。然而,对于大多数情况,使用AsParallel可能仍然是一个更好的选择 - 简单的循环体通常意味着某种映射/归约操作,并且AsParallel总的来说,LINQ 非常擅长这一点。例如,您的示例代码可以简单地重写为:

var result = testData.AsParallel().Where(i => i == 1).ToList();

唯一使用的情况AsParallel与所有其他 LINQ 一样,这是一个坏主意 - 当您的循环体有副作用时。有些可能是可以忍受的,但完全避免它们会更安全。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

任务并行不稳定,有时使用 100% CPU 的相关文章

  • 如何将 std::string& 转换为 C# 引用字符串

    我正在尝试将 C 函数转换为std string参考C 我的 API 如下所示 void GetStringDemo std string str 理想情况下 我希望在 C 中看到类似的东西 void GetStringDemoWrap r
  • 在一个数据访问层中处理多个连接字符串

    我有一个有趣的困境 我目前有一个数据访问层 它必须与多个域一起使用 并且每个域都有多个数据库存储库 具体取决于所调用的存储过程 目前 我只需使用 SWITCH 语句来确定应用程序正在运行的计算机 并从 Web config 返回适当的连接字
  • 无限循环与无限递归。两者都是未定义的吗?

    无副作用的无限循环是未定义的行为 看here https coliru stacked crooked com view id 24e0a58778f67cd4举个例子参考参数 https en cppreference com w cpp
  • C++ 多行字符串原始文字[重复]

    这个问题在这里已经有答案了 我们可以像这样定义一个多行字符串 const char text1 part 1 part 2 part 3 part 4 const char text2 part 1 part 2 part 3 part 4
  • 方程“a + bx = c + dy”的积分解

    在等式中a bx c dy 所有变量都是整数 a b c and d是已知的 我如何找到整体解决方案x and y 如果我的想法是正确的 将会有无限多个解 由最小公倍数分隔b and d 但我只需要一个解决方案 我可以计算其余的 这是一个例
  • 人脸 API DetectAsync 错误

    我想创建一个简单的程序来使用 Microsoft Azure Face API 和 Visual Studio 2015 检测人脸 遵循 https social technet microsoft com wiki contents ar
  • 使用 C# 中的 CsvHelper 将不同文化的 csv 解析为十进制

    C 中 CsvHelper 解析小数的问题 我创建了一个从 byte 而不是文件获取 csv 文件的类 并且它工作正常 public static List
  • x:将 ViewModel 方法绑定到 DataTemplate 内的事件

    我基本上问同样的问题这个人 https stackoverflow com questions 10752448 binding to viewmodels property from a template 但在较新的背景下x Bind V
  • 为什么 C# 2.0 之后没有 ISO 或 ECMA 标准化?

    我已经开始学习 C 并正在寻找标准规范 但发现大于 2 0 的 C 版本并未由 ISO 或 ECMA 标准化 或者是我从 Wikipedia 收集到的 这有什么原因吗 因为编写 审查 验证 发布 处理反馈 修订 重新发布等复杂的规范文档需要
  • LINQ:使用 INNER JOIN、Group 和 SUM

    我正在尝试使用 LINQ 执行以下 SQL 最接近的是执行交叉联接和总和计算 我知道必须有更好的方法来编写它 所以我向堆栈团队寻求帮助 SELECT T1 Column1 T1 Column2 SUM T3 Column1 AS Amoun
  • 如何在当前 Visual Studio 主机内的 Visual Studio 扩展中调试使用 Roslyn 编译的代码?

    我有一个 Visual Studio 扩展 它使用 Roslyn 获取当前打开的解决方案中的项目 编译它并从中运行方法 程序员可以修改该项目 我已从当前 VisualStudioWorkspace 成功编译了 Visual Studio 扩
  • 复制目录下所有文件

    如何将一个目录中的所有内容复制到另一个目录而不循环遍历每个文件 你不能 两者都不Directory http msdn microsoft com en us library system io directory aspx nor Dir
  • 为什么 isnormal() 说一个值是正常的,而实际上不是?

    include
  • 为什么 std::uint32_t 与 uint32_t 不同?

    我对 C 有点陌生 我有一个编码作业 很多文件已经完成 但我注意到 VS2012 似乎有以下语句的问题 typedef std uint32 t identifier 不过 似乎将其更改为 typedef uint32 t identifi
  • C++ 中的参考文献

    我偶尔会在 StackOverflow 上看到代码 询问一些涉及函数的重载歧义 例如 void foo int param 我的问题是 为什么会出现这种情况 或者更确切地说 你什么时候会有 对参考的参考 这与普通的旧参考有何不同 我从未在现
  • DotNetZip:如何提取文件,但忽略zip文件中的路径?

    尝试将文件提取到给定文件夹 忽略 zip 文件中的路径 但似乎没有办法 考虑到其中实现的所有其他好东西 这似乎是一个相当基本的要求 我缺少什么 代码是 using Ionic Zip ZipFile zf Ionic Zip ZipFile
  • 指针和内存范围

    我已经用 C 语言编程有一段时间了 但对 C 语言还是很陌生 有时我对 C 处理内存的方式感到困惑 考虑以下有效的 C 代码片段 const char string void where is this pointer variable l
  • 类型或命名空间“MyNamespace”不存在等

    我有通常的类型或命名空间名称不存在错误 除了我引用了程序集 using 语句没有显示为不正确 并且我引用的类是公共的 事实上 我在不同的解决方案中引用并使用相同的程序集来执行相同的操作 并且效果很好 顺便说一句 这是VS2010 有人有什么
  • 如何确定 CultureInfo 实例是否支持拉丁字符

    是否可以确定是否CultureInfo http msdn microsoft com en us library system globalization cultureinfo aspx我正在使用的实例是否基于拉丁字符集 我相信你可以使
  • 使用 WGL 创建现代 OpenGL 上下文?

    我正在尝试使用 Windows 函数创建 OpenGL 上下文 现代版本 基本上代码就是 创建窗口类 注册班级 创建一个窗口 choose PIXELFORMATDESCRIPTOR并设置它 创建旧版 OpenGL 上下文 使上下文成为当前

随机推荐

  • 有没有办法使用 perf 工具查找流程中各个功能的性能?

    我正在尝试在流程中实现各个功能的性能 我该如何使用 perf 工具来做到这一点 还有其他工具吗 例如 假设 main 函数调用函数 A B C 我想分别获得主要功能以及功能 A B C 的性能 有没有一个很好的文档来了解 perf 源代码
  • Tomcat 上的 Grails - 如何记录原始 HTTP 请求/响应

    我找不到配置我的虚拟教程 Grails 应用程序来记录 Grails 服务器 实际上是 Tomcat 接受 生成的所有 HTTP 请求和响应的方法 这可能吗 另一种选择是使用 tomcat 的内置访问日志记录 http tomcat apa
  • 如何首先使用 msbuild 构建依赖项目

    我刚刚开始研究 msbuild 因为我想制作自己的构建脚本 目前 我可以创建仅编译一个项目的构建脚本 但如何处理依赖项 例如 如果我有两个使用这两个 msbuild 脚本构建的项目怎么办 项目A xml 项目B xml 如何告诉 msbui
  • 用于构建“调试”和“发布”JAR 文件的惯用 Gradle 脚本

    我正在尝试创建一个 Gradle 构建脚本来构建 Java jar文件处于 发布 或 调试 模式 并且在参数化脚本时遇到问题 问题是 使用 Java 插件在 Gradle 脚本中执行此操作的惯用方法是什么 或者 如果没有惯用的方法 那么真正
  • FCM flutter 启用通知振动

    我正在为 Android 和 IOS 开发 Flutter 应用程序 我已经根据这个为Android创建了通知渠道article https rechor medium com creating notification channels
  • 类型 x 比值更难访问

    这是我的代码的抽象 module RootModule module private SubModule I want everything in this module to be inaccessible from outside th
  • Heroku 上带有 Django Channels 的 Websocket

    我正在尝试将我的应用程序部署到heroku 该应用程序有一个简单的聊天系统 使用 Websockets 和 django 通道 当我使用 python manage py runserver 测试我的应用程序时 应用程序的行为正如预期的那样
  • 处理时间跨度的最佳方式?

    PHP 中是否有处理时间跨度的首选类或方法 我感兴趣的主要功能是检查日期是否在时间跨度内 或生成下限和上限的时间戳 使用unix时间戳 如果是mysql数据 那么你可以像这样存储时间戳 如果不是 那么你也可以将mysql日期时间转换为uni
  • 如何使表格中一行的最后一个单元格占据所有剩余宽度

    在下面的 HTML 片段中 如何使包含 LAST 的列的宽度占据行的其余部分 并且包含 COLUMN ONE 和 COLUMN TWO 的列的宽度足以包含其内容 并且不更大 谢谢 table border collapse collapse
  • Gorm 中的级联删除不会删除关联表

    我是戈尔姆的新手 我试图进行级联删除 如果我删除一个用户 与该用户关联的角色 属于 个人资料 有一个 和书籍 一对多 也将被删除 我在下面设置了模型 但级联似乎不起作用 当我删除用户时 角色 个人资料和书籍仍保留在数据库中 而不是被删除 软
  • 单击元素以外的任意位置以使用 if 语句隐藏它

    解决方案如下 我已经阅读了这里有关这个概念的大多数问题 但我似乎无法让它与 if 语句一起使用 有什么帮助吗 JSFiddle http jsfiddle net 2udYp button click function div fadeTo
  • 如何通过 Swift 包管理器在 Xcode 中安装包

    我正在 Xcode 中开发一个项目 并尝试安装和使用加密货币Swift https github com krzyzanowskim CryptoSwift通过 Swift 包管理器进行包 我读了文档 https swift org pac
  • CMake:不支持的 GNU 版本 - 不支持高于 8 的 gcc 版本

    在降级我的 GCC 之前 我想知道是否有一种方法可以确定我的机器中的哪些程序 框架或依赖项将被破坏 以及是否有更好的方法来安装 openpose 例如 更改 CMake 中的某些内容 有没有办法可以解决这个问题 而无需更改我的系统 GCC
  • 同时使用 :nth-child 和 :nth-last-child

    我做不到 nth child and nth last child伪类同时工作 效果很好 突出显示前 3 个要素 a li nth child n 3 background fbfcc8 效果很好 突出显示最后 3 个元素 b li nth
  • Android:如何以原始尺寸显示图像

    我从字节数组创建位图图像并将其显示在 ImageView 中 这android layout width and android layout heightImageView 的设置为wrap content 并且android scale
  • 通过属性和正文指定 JSTL 值之间的区别

    我试图弄清楚 JSTL 的这两种用途之间是否存在功能差异
  • 电话号码的 jQuery 输入掩码

    我希望用户的输入自动填充电话号码的标点符号 以便看起来像这样 xxx xxx xxxx 这是我的 HTML 代码 div class form group div
  • UIManagedDocument 迁移数据模型

    我正在开发一个 iPhone 应用程序 它使用UIManagedDocument并将其文档存储在 iCloud 上 一切都工作正常 直到我更改了我的核心数据模型 方案 添加了新的模型版本 就像我在过去几周内多次所做的那样 我添加了一个新属性
  • 如何使用python Bottle框架获取客户端IP地址

    我需要使用 python 的客户端 IP 地址 我已经尝试过下面的代码 但它在服务器中不起作用 from socket import gethostname gethostbyname ip gethostbyname gethostnam
  • 任务并行不稳定,有时使用 100% CPU

    我目前正在测试 C 的 Parallel 一般来说 它工作得很好 并且使用并行比普通的 foreach 循环更快 然而 有时 比如五分之一 我的 CPU 会达到 100 使用率 导致并行任务非常慢 我的 CPU 设置是 i5 4570 和