共享内存 & 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 { PropagateCompletion = 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 = 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并发模型(一)
学习资料来自http://ifeve.com/java-concurrency-thread-directory/ 一.多线程 进程和线程的区别: 一个程序运行至少一个进程,一个进程至少包含一个线程. ...
- 【CUDA 基础】5.1 CUDA共享内存概述
title: [CUDA 基础]5.1 CUDA共享内存概述 categories: - CUDA - Freshman tags: - CUDA共享内存模型 - CUDA共享内存分配 - CUDA共 ...
- TensorRT 3:更快的TensorFlow推理和Volta支持
TensorRT 3:更快的TensorFlow推理和Volta支持 TensorRT 3: Faster TensorFlow Inference and Volta Support 英伟达Tens ...
- Linux系统编程之命名管道与共享内存
在上一篇博客中,我们已经熟悉并使用了匿名管道,这篇博客我们将讲述进程间通信另外两种常见方式--命名管道与共享内存. 1.命名管道 管道是使用文件的方式,进行进程之间的通信.因此对于管道的操作,实际上还 ...
- SharePoint 2010中使用SPListItemCollectionPosition更快的结果
转:http://www.16kan.com/article/detail/318657.html Introduction介绍 In this article we will explore the ...
- UnixIPC之共享内存
Unix-IPC之共享内存 一,共享内存的概念 共享内存通信技术是一种最快的可用IPC形式,它是针对其他通信机制运行效率低和设计的新型通信技术(其他的如:信号量,管道,套接字等).这种通信技术往往与其 ...
- 利用共享内存实现比NCCL更快的集合通信
作者:曹彬 | 旷视 MegEngine 架构师 简介 从 2080Ti 这一代显卡开始,所有的民用游戏卡都取消了 P2P copy,导致训练速度显著的变慢.针对这种情况下的单机多卡训练,MegEng ...
- Akka系列(四):Akka中的共享内存模型
前言...... 通过前几篇的学习,相信大家对Akka应该有所了解了,都说解决并发哪家强,JVM上面找Akka,那么Akka到底在解决并发问题上帮我们做了什么呢? 共享内存 众所周知,在处理并发问题上 ...
随机推荐
- 【模拟8.11】将军令(贪心&&树形DP)
只看45分的话,是树形DP....(当然也有能拿到70分+的大佬) 40分: 只考虑k==1的情况,树形DP 所以每个节点可能被父亲,自己,儿子控制 设f[MAXN][3],0表示儿子,1表示自己,2 ...
- NOIP模拟测试39,思维禁锢专场「工业题·玄学题·卡常题」
工业题 题解 抱歉,题解没时间写了 代码 #include<bits/stdc++.h> using namespace std; #define ll long long #define ...
- 在C++中,你真的会用new吗?
摘要:"new"是C++的一个关键字,同时也是操作符.关于new的话题非常多,因为它确实比较复杂,也非常神秘. 本文分享自华为云社区<如何编写高效.优雅.可信代码系列(2)- ...
- 『心善渊』Selenium3.0基础 — 12、Selenium操作鼠标和键盘事件
目录 (一)对鼠标的操作 1.鼠标事件介绍 2.ActionChains 类鼠标操作的常用方法 3.perform()方法 4.鼠标事件操作步骤 5.示例 (1)右键单击.左键双击 (2)鼠标拖拽动作 ...
- 关于基于Nexus3和Docker搭建私有Nuget服务的探索
背景简介 NuGet是Microsoft开发平台的程序集包管理器,它由客户端工具和服务端站点组成,客户端工具提供给用户管理和安装/卸载软件程序包,以及打包和发布程序包到NuGet服务端站点等功能,服务 ...
- 精尽Spring Boot源码分析 - 配置加载
该系列文章是笔者在学习 Spring Boot 过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring Boot 源码分析 GitHub 地址 进行阅读 Sprin ...
- 『心善渊』Selenium3.0基础 — 21、Selenium实现绕过验证码进行登陆
目录 1.验证码问题 2.处理验证码的方法 3.Selenium绕过验证码登陆的实现 4.总结 5.补充练习 1.验证码问题 对于Web应用来说,大部分的系统在用户登录时,都要求用户输入验证码.验证码 ...
- Mysql索引降维 优化查询 提高效率
在前一篇文章中,我们已经介绍了索引.索引的优化规则等等 原文链接:Siam博客 mysql索引优化 在其中我们有引申出组合索引,多个单字段索引冲突两个知识点. 本文章主要是与后者有关联. 在原文中,我 ...
- mysql日期时间处理
获得当前周的周一到周日 select subdate(curdate(),date_format(curdate(),'%w')-1)//获取当前日期在本周的周一 select subdate(c ...
- buu Youngter-drive
一.查壳,发现是upx的壳,用自解压方式,脱下壳 二.之后发现打不开了,应该是要修复,不想修复了,直接拖入ida 找到关键函数,中间发生一点小插曲,发现堆栈不平衡,然后导致F5反编译失败,百度了下是A ...