任务并行不稳定,有时使用 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 的相关文章

  • 将列表元素分组到字典中

    我有一个包含 8 个元素的列表 ConfigFile ControllerList 该列表的类型为 List
  • 读取进程的进程内存不会返回所有内容

    我正在尝试扫描第三方应用程序的内存 我已经查到地址了 现在是在0x0643FB78 问题是 从那以后我就再也爬不上去LPMODULEENTRY32 gt modBaseAddr is 0x00400000 and LPMODULEENTRY
  • C# 如何比较两个字符串并指出哪些部分不同

    例如 如果我有 string a personil string b personal 我想得到 string c person i l 然而 它不一定是单个字符 我也可以这样 string a disfuncshunal string b
  • JSON 值无法转换为 System.Collections.Generic.List

    我正在尝试列出游戏中的所有项目using System Text Json 我对使用 json 文件很陌生 我尝试这样做来测试它是否有效 List
  • Task.Delay 是否真的像 I/O 操作一样异步,即它依赖于硬件和中断而不是线程?

    我发现了大量相关内容 但这些内容都是拐弯抹角的 但我始终无法找到答案 我几乎 100 确定Task Delay int 不使用线程 因为我可以在只有 16 个逻辑处理器的机器上运行此代码 var tasks new List
  • 这些比较应该返回什么?

    我有一个使用 string compare string string 对某些值进行排序的应用程序 我不明白的是为什么 1022 比较小于 10 23 而 10 23 比较小于 1024 是否有特定于 值的东西导致了这个结果 string
  • 为什么 Resources.Load 返回 null?

    我的项目有多个精灵 位于 Assets Sprites 中 我想使用 C 脚本加载它们 我已经测试过这个 Sprite myFruit Resources Load
  • Xamarin 中 QR 扫描后的处理对话框

    我在Xamarin应用程序中使用QR码扫描仪 当它扫描QR码时 它会执行一些操作 大约需要一分钟 而在执行操作时 我想在屏幕上显示一个加载对话框 但是 它没有显示在屏幕上 并且在应用程序的其他地方 它运行得很好 Code var expec
  • 如何在cmake中添加cuda源代码的定义

    我使用的是 Visual Studio 2013 Windows 10 CMake 3 5 1 一切都可以使用标准 C 正确编译 例如 CMakeLists txt project Test add definitions D WINDOW
  • Oracle ODP.Net 与实体框架 6 - 从表视图中选择时出现 ORA-00955

    我创建了两个应用程序 第一个使用 ODP Net 另一个没有实体 效果很好 static void Main string args OracleConnection con new OracleConnection using conne
  • 托管 C++ 和 AnyCPU

    我有一个托管 C dll 我从 C 项目中引用它 C 项目将被编译为 AnyCPU 有没有办法编译 32 位和 64 位版本的托管 C dll 然后告诉 C 项目在运行时根据正在运行的体系结构加载正确的版本 让 AnyCPU dll 与 C
  • 将supportedRuntime嵌入到exe文件中

    我需要将仅包含supportedRuntime 设置的app config 文件嵌入到我的exe 文件中 我尝试执行构建操作嵌入资源 但它现在没有从配置文件中读取值 并且它不起作用 这是我的配置文件
  • 如何保存具有多个缩进设置的XmlDocument?

    我需要保存一个XmlDocument以适当的缩进归档 Formatting Indented 但有些节点及其子节点必须排在一行 Formatting None 从那时起如何实现这一目标XmlTextWriter接受整个文档的设置 在 Ahm
  • 使用 boost 几何检查两条线是否有交点

    是否可以使用 boost geometry 检查两条线段 每条线段由二维中的两个点给出 是否彼此相交 如果可能的话 boost geometry 是否还允许检查特殊情况 例如另一条线上只有一个点 数字上 或者两条线相等 如果你具体谈论Boo
  • c++ string::size 中的 CharT 元素是什么?

    From http en cppreference com w cpp string basic string size http en cppreference com w cpp string basic string size 的数量
  • 以编程方式将 Word 文件另存为图片

    我想将Word文档的第一页另存为图片 使用 C 有什么方法可以做到这一点 您可以将 Word 文档打印到 XPS 文档 在 WPF Net 3 5 应用程序中打开它 并使用 WPF 框架的文档和图像功能将第一个内部固定页面对象转换为位图 如
  • 在 C# 中同步闪烁标签

    我创建了一个BlinkingLabel类 源自Forms Label 其中有一个Forms Timer这允许我启用和禁用闪烁效果 我创建了 4 个标签BlinkingLabel类型 我的问题是 如果所有 4 个标签在不同时间闪烁 则闪烁效果
  • 绑定到 ListView 项目从视图模型中点击的属性

    我正在尝试使用 itemtapped 属性将事件绑定到菜单页面上的 ListView 目前我在我的应用程序中使用 MVVM Xamarin 表单实验室 框架 我想要完成的是当用户点击菜单项时应用程序导航到正确的视图 这是xaml代码
  • 如何为 Blazor MapFallbackToFile() 生成正确的错误

    我有一个项目想要用作 Web API 和 Blazor wasm UI 该 API 也可以从其他项目访问 因此我希望该 API 向消费者提供有用的错误详细信息 我现在通过使用该网站来实现这两个目的MapFallbackToFile 方法 但
  • 在 C 中打印字符串的所有排列

    我正在学习回溯和递归 并且我陷入了打印字符串所有排列的算法 我用以下方法解决了它贝尔算法 http programminggeeks com bell algorithm for permutation 用于排列 但我无法理解递归方法 我在

随机推荐

  • 有没有办法使用 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 和