共享内存 & Actor并发模型哪个更快?
HI,前几天被.NET圈纪检委@懒得勤快问到
共享内存和Actor并发模型哪个速度更快。
前文传送门:
说实在,我内心10w头羊驼跑过......
先说结论
- 首先两者对于并发的风格模型不一样。
共享内存利用多核CPU的优势,使用强一致的锁机制控制并发, 各种锁交织,稍不注意可能出现死锁,更适合熟手。
Actor模型易于控制和管理,以消息触发,流水线挨个处理, 思路清晰。
- 真要说性能,
求100000 以内的素数的个数]场景
&我电脑8c 16g的配置
, 我根据这个示例拍脑袋对比。。。。。
-
2.1 理论上如果以默认的Actor并发模型来做这个事情,Actor的性能是逊于共享内存模型的;
-
2.2 上文中我对于Actor做了多线程优化,性能慢慢追上来了。
默认Actor模型
计算[100_000内素数的个数], 分为两步:
(1) 迭代判断当前数字是不是素数
(2) 如果是素数,执行sum++
共享内存完成以上两步, 均能充分利用CPU多核心。
Actor模型:与TPL中的原语不同,TPL datflow中的所有块默认是单线程的,这就意味着完成以上两步的
TransfromBlock和
ActionBlock都是以一个线程挨个处理消息数据(这也是Dataflow的设计初衷,形成清晰单纯的流水线)。
猜测起来也是共享内存相比默认的Actor模型更具优势。
使用NUnit做单元测试,数据量从小到大: 10_000,50_000,100_000,200_000,300_000,500_000
using NUnit.Framework; using System; using System.Threading.Tasks; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks.Dataflow; namespace TestProject2 { public class Tests { [TestCase(10_000)] [TestCase(50_000)] [TestCase(100_000)] [TestCase(200_000)] [TestCase(300_000)] [TestCase(500_000)] public void ShareMemory(int num) { var sum = 0; Parallel.For(1, num + 1, (x, state) => { var f = true; if (x == 1) f = false; for (int i = 2; i <= x / 2; i++) { if (x % i == 0) // 被[2,x/2]任一数字整除,就不是质数 f = false; } if (f == true) { Interlocked.Increment(ref sum);// 共享了sum对象,“++”就是调用sum对象的成员方法 } }); Console.WriteLine($"1-{num}内质数的个数是{sum}"); } [TestCase(10_000)] [TestCase(50_000)] [TestCase(100_000)] [TestCase(200_000)] [TestCase(300_000)] [TestCase(500_000)] public async Task Actor(int num) { var linkOptions = new DataflowLinkOptions { PropagateCompletio ad8 n = true }; var bufferBlock = new BufferBlock<int>(); var transfromBlock = new TransformBlock<int, bool>(x => { var f = true; if (x == 1) f = false; for (int i = 2; i <= x / 2; i++) { if (x % i == 0) // 被[2,x/2]任一数字整除,就不是质数 f = false; } return f; }, new ExecutionDataflowBlockOptions { EnsureOrdered = false }); var sum = 0; var actionBlock = new ActionBlock<bool>(x => { if (x == true) sum++; }, new ExecutionDataflowBlockOptions { EnsureOrdered = false }); transfromBlock.LinkTo(actionBlock, linkOptions); // 准备从pipeline头部开始投递 try { var list = new List<int> { }; for (int i = 1; i <= num; i++) { var b = await transfromBlock.SendAsync(i); if (b == false) { list.Add(i); } } if (list.Count > 0) { Console.WriteLine($"md,num post failure,num:{list.Count},post again"); // 再投一次 foreach (var item in list) { transfromBlock.Post(item); } } transfromBlock.Complete(); // 通知头部,不再投递了; 会将信息传递到下游。 actionBlock.Completion.Wait(); // 等待尾部执行完 Console.WriteLine($"1-{num} Prime number include {sum}"); } catch (Exception ex) { Console.WriteLine($"1-{num} cause exception.",ex); } } } }
测试结果如下:
测试结果印证我说的结论2.1
优化后的Actor模型
那后面我对Actor做了什么优化呢?能产生下图的结论。
请重新回看《三分钟掌握》 TransformBlock块的细节:
var transfromBlock = new TransformBlock<int, bool>(x => { var f = true; if (x == 1) f = false; for (int i = ad8 2; i <= x / 2; i++) { if (x % i == 0) // 被[2,x/2]任一数字整除,就不是质数 f = false; } return f; }, new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism=50, EnsureOrdered = false });
上面说到默认的Actor是单线程处理输入的消息, 此时我们设置了
MaxDegreeOfParallelism参数,参数能在Actor中开启多线程并发执行,但是这里面就不能有共享变量(否则你又得加锁),恰好我们完成
(1) 迭代判断当前数字是不是素数这一步并不依赖共享对象,所以这一步性能与共享内存模型基本没差别。
那为什么总体性能慢慢超过共享内存?
这是因为执行第二步
(2) 如果是素数,执行sum++, 共享内存要加解锁,线程上下文切换,而Actor单线程挨个处理, 总体就略胜共享内存模型了。
这里再次强调,Actor模型执行第二步
(2) 如果是素数,执行sum++,不可开启MaxDegreeOfParallelism,因为依赖了共享变量sum
结束语
请大家仔细对比结论和上图,脱离场景和硬件环境谈性能就是耍流氓,理解不同并发模型的风格和能力是关键,本文仅针对这个示例拍脑袋对比。
实际要针对场景和未来的拓展性、可维护性、可操作性做技术选型 。
That's All, 感谢.NET圈纪检委@懒得勤快促使我重温了单元测试的写法 & 深度分析Actor模型。
- Actor 并发模型 & "不要通过共享内存来通信,而应该通过通信来共享内存"
- Actor并发模型&基于共享内存线程模型
- Actor并发模型&基于共享内存线程模型
- 【Java并发编程】6、volatile关键字解析&内存模型&并发编程中三概念
- Go 语言的并发模型--通过通信来共享内存
- volatile关键字解析&内存模型&并发编程中三概念
- 并发模型—共享内存模型(线程与锁)示例篇
- 基于多线程的共享内存模型和基于actor的消息通信模型
- 并发模型之——共享内存模型(线程与锁)理论篇
- volatile关键字解析&内存模型&并发编程中三概念
- 进程间通信:消息队列&信号量&共享内存
- Kilim框架的Actor并发模型
- JVM并发机制的探讨——内存模型、内存可见性和指令重排序
- JVM并发机制探讨—内存模型、内存可见性和指令重排序
- 高并发分布式事务解决之道-Actor模型(附Akka与Reactor比较)
- Java基础复习一之多线程(并发,内存模型)
- future相比于其他并发设计模式的优势(“回调驱动(多线程环境下)”、“消息/事件驱动(Actor模型中))
- linux内存模型之bootmem分配器<一>
- PHP下用Swoole实现Actor并发模型的方法
- 并行编程:共享内存模型与分布式内存模型