基于 CAS 无锁实现的 Disruptor.NET 居然慢于 BlockingCollection,是真的吗?
StackOverflow 有人说自己的Disruptor.NET 代码比 BlockingCollection 还有慢 2 倍,并且把完整代码贴出,楼下几个老外也的回复说了一堆,但是没研究出个所以然来,讨论到最后甚至说可能你的场景不适合 Disruptor,我对此表示怀疑,BlockingCollection 内部实现比较简单粗暴,必要时就加锁,取数据时用信号量等待添加操作完成,而 Disruptor 却不是一般的实现,是专门针对 CPU 缓存的特性优化过的,内部没有锁只有 CAS 原子操作,而且还考虑到了 false sharing,因此理论上 Disruptor 不会比 BlockingCollection 慢。
可是既然实际应用上出现问题,那就要分析下原因了。
把他的代码弄下来看了一下,问题多多啊。
在 Disruptor EventHandler 里面不定时调用 Console.WriteLine ,但是在 BlockingCollection 的 Handler 里面却只是记录了数据, Console.WriteLine 内部可是有锁的,调用的开销很大,如何能取得公平的结果呢?
另外 RingBuffer 的 Size 太小只有 64,严重影响 Disruptor 的表现,实际测试对比下来,应该 1024 或更大。
还有 BlockingCollection 里面的 while (!dataItems.IsCompleted) 写的也有问题,即使 BlockingCollection Producer 在循环中一直做添加操作,BlockingCollection 内部状态也并不是一直在添加状态中,这样导致添加循环还没做完,可是计时器的循环已经提前结束,导致 BlockingCollection 测得时间少于实际实际。
Task.Factory.StartNew(() => {
while (!dataItems.IsCompleted)
{
ValueEntry ve = null;
try
{
ve = dataItems.Take();
long microseconds = sw[ve.Value].ElapsedTicks / (Stopwatch.Frequency / (1000L * 1000L));
results[ve.Value] = microseconds;
//Console.WriteLine("elapsed microseconds = " + microseconds);
//Console.WriteLine("Event handled: Value = {0} (processed event {1}", ve.Value, ve.Value);
}
catch (InvalidOperationException) { }
}
}, TaskCreationOptions.LongRunning);
for (int i = ; i < length; i++)
{
var valueToSet = i;
ValueEntry entry = new ValueEntry();
entry.Value = valueToSet;
sw[i].Restart();
dataItems.Add(entry);
//Console.WriteLine("Published entry {0}, value {1}", valueToSet, entry.Value);
//Thread.Sleep(1000);
}
然后重新修改了他的代码,实测 Disruptor 10 倍速度于 BlockingCollection (这里插一句题外话,Disruptor .NET 版本的速度全面快于 Java 版本,不少场景下的速度比 Java 版本要快 10 倍,.NET 版是从 Java 移植过来的实现也和 Java 保持一直,是哪些语言特性导致性能差异这么大呢?)。
Disruptor is 10x faster than BlockingCollection with multi producer (10 parallel producet), 2x faster than BlockingCollection with Single producer:
using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using Disruptor;
using Disruptor.Dsl;
using NUnit.Framework; namespace DisruptorTest.Ds
{
public sealed class ValueEntry
{
internal int Id { get; set; }
} class MyHandler : IEventHandler<ValueEntry>
{
public void OnEvent(ValueEntry data, long sequence, bool endOfBatch)
{
}
} [TestFixture]
public class DisruptorPerformanceTest
{
private volatile bool collectionAddEnded; private int producerCount = ;
private int runCount = ;
private int RingBufferAndCapacitySize = ; [TestCase()]
public async Task TestBoth()
{
for (int i = ; i < ; i++)
{
foreach (var rs in new int[] {, , , /*,4096,4096*2*/})
{
Console.WriteLine($"RingBufferAndCapacitySize:{rs}, producerCount:{producerCount}, runCount:{runCount} of {i}");
RingBufferAndCapacitySize = rs;
await DisruptorTest();
await BlockingCollectionTest();
}
}
} [TestCase()]
public async Task BlockingCollectionTest()
{
var sw = new Stopwatch();
BlockingCollection<ValueEntry> dataItems = new BlockingCollection<ValueEntry>(RingBufferAndCapacitySize); sw.Start(); collectionAddEnded = false; // A simple blocking consumer with no cancellation.
var task = Task.Factory.StartNew(() =>
{
while (!collectionAddEnded && !dataItems.IsCompleted)
{
//if (!dataItems.IsCompleted && dataItems.TryTake(out var ve))
if (dataItems.TryTake(out var ve))
{
}
}
}, TaskCreationOptions.LongRunning); var tasks = new Task[producerCount];
for (int t = ; t < producerCount; t++)
{
tasks[t] = Task.Run(() =>
{
for (int i = ; i < runCount; i++)
{
ValueEntry entry = new ValueEntry();
entry.Id = i;
dataItems.Add(entry);
}
});
} await Task.WhenAll(tasks); collectionAddEnded = true;
await task; sw.Stop(); Console.WriteLine($"BlockingCollectionTest Time:{sw.ElapsedMilliseconds/1000d}");
} [TestCase()]
public async Task DisruptorTest()
{
var disruptor =
new Disruptor.Dsl.Disruptor<ValueEntry>(() => new ValueEntry(), RingBufferAndCapacitySize, TaskScheduler.Default,
producerCount > ? ProducerType.Multi : ProducerType.Single, new BlockingWaitStrategy());
disruptor.HandleEventsWith(new MyHandler()); var _ringBuffer = disruptor.Start(); Stopwatch sw = Stopwatch.StartNew(); sw.Start(); var tasks = new Task[producerCount];
for (int t = ; t < producerCount; t++)
{
tasks[t] = Task.Run(() =>
{
for (int i = ; i < runCount; i++)
{
long sequenceNo = _ringBuffer.Next();
_ringBuffer[sequenceNo].Id = ;
_ringBuffer.Publish(sequenceNo);
}
});
} await Task.WhenAll(tasks); disruptor.Shutdown(); sw.Stop();
Console.WriteLine($"DisruptorTest Time:{sw.ElapsedMilliseconds/1000d}s");
}
}
}
BlockingCollectionTest with a shared ValueEntry instance (no new ValueEntry() in for loop)
RingBufferAndCapacitySize:64, producerCount:10, runCount:1000000 of 0
DisruptorTest Time:16.962s
BlockingCollectionTest Time:18.399
RingBufferAndCapacitySize:512, producerCount:10, runCount:1000000 of 0 DisruptorTest Time:6.101s
BlockingCollectionTest Time:19.526
RingBufferAndCapacitySize:1024, producerCount:10, runCount:1000000 of 0
DisruptorTest Time:2.928s
BlockingCollectionTest Time:20.25
RingBufferAndCapacitySize:2048, producerCount:10, runCount:1000000 of 0
DisruptorTest Time:2.448s
BlockingCollectionTest Time:20.649
BlockingCollectionTest create a new ValueEntry() in for loop
RingBufferAndCapacitySize:64, producerCount:10, runCount:1000000 of 0
DisruptorTest Time:27.374s
BlockingCollectionTest Time:21.955
RingBufferAndCapacitySize:512, producerCount:10, runCount:1000000 of 0
DisruptorTest Time:5.011s
BlockingCollectionTest Time:20.127
RingBufferAndCapacitySize:1024, producerCount:10, runCount:1000000 of 0
DisruptorTest Time:2.877s
BlockingCollectionTest Time:22.656
RingBufferAndCapacitySize:2048, producerCount:10, runCount:1000000 of 0
DisruptorTest Time:2.384s
BlockingCollectionTest Time:23.567
基于 CAS 无锁实现的 Disruptor.NET 居然慢于 BlockingCollection,是真的吗?的更多相关文章
- (转载)java高并发:CAS无锁原理及广泛应用
java高并发:CAS无锁原理及广泛应用 版权声明:本文为博主原创文章,未经博主允许不得转载,转载请注明出处. 博主博客地址是 http://blog.csdn.net/liubenlong007 ...
- java并发:AtomicInteger 以及CAS无锁算法【转载】
1 AtomicInteger解析 众所周知,在多线程并发的情况下,对于成员变量,可能是线程不安全的: 一个很简单的例子,假设我存在两个线程,让一个整数自增1000次,那么最终的值应该是1000:但是 ...
- CAS无锁机制原理
原子类 java.util.concurrent.atomic包:原子类的小工具包,支持在单个变量上解除锁的线程安全编程 原子变量类相当于一种泛化的 volatile 变量,能够支持原子的和有条件的读 ...
- CAS无锁算法与ConcurrentLinkedQueue
CAS:Compare and Swap 比较并交换 java.util.concurrent包完全建立在CAS之上的,没有CAS就没有并发包.并发包借助了CAS无锁算法实现了区别于synchroni ...
- CAS无锁技术
前言:关于同步,很多人都知道synchronized,Reentrantlock等加锁技术,这种方式也很好理解,是在线程访问的临界区资源上建立一个阻塞机制,需要线程等待 其它线程释放了锁,它才能运行. ...
- CAS无锁实现原理以及ABA问题
CAS(比较与交换,Compare and swap) 是一种有名的无锁算法.无锁编程,即不使用锁的情况下实现多线程之间的变量同步,也就是在没有线程被阻塞的情况下实现变量的同步,所以也叫非阻塞同步(N ...
- 探索CAS无锁技术
前言:关于同步,很多人都知道synchronized,Reentrantlock等加锁技术,这种方式也很好理解,是在线程访问的临界区资源上建立一个阻塞机制,需要线程等待 其它线程释放了锁,它才能运行. ...
- CAS无锁模式
一.java内存模型:JMM 在内存模型当中定义一个主内存,所有声明的实例变量都存在于主内存当中,主内存的数据会共享给所有线程,每一个线程有一个块工作内存,工作内存当中主内存数据的副本当更新数据时,会 ...
- 无锁并发框架Disruptor学习入门
刚刚听说disruptor,大概理一下,只为方便自己理解,文末是一些自己认为比较好的博文,如果有需要的同学可以参考. 本文目标:快速了解Disruptor是什么,主要概念,怎么用 1.Disrupto ...
随机推荐
- centos7—计划任务(at、cron)
centos7—计划任务(at.cron) 2018-08-08 14:33:17 coisini_覔 阅读数 3751更多 分类专栏: Linux基础 crond/at 版权声明:本文为博主原创 ...
- 【CentOS7】设置静态IP地址
[CentOS7]设置静态IP地址 转载:https://www.cnblogs.com/yangchongxing/p/10645871.html 图像化修改 nmtui 查看当前网卡名称 # if ...
- hdu 6299 Balanced Sequence (贪心)
Balanced Sequence Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others ...
- Codeforces Round #592 (Div. 2)
A. Pens and Pencils 题目链接:https://codeforces.com/contest/1244/problem/A 题意: 给定五个数 a , b , c , d , k 求 ...
- 使用mongoshake进行oplog同步读取,解决乱码问题
mongoshake 是个开源的用户mongo数据迁移和同步的工具,支持往各种目标源头写数据 具体:https://github.com/alibaba/MongoShake 有业务场景想把oplog ...
- Android获取ROOT权限的通用方法
背景 自从Android问世以后,给手机获取ROOT权限变成了玩机爱好者老生常谈的话题.拥有手机,却不能拥有操作手机的最高权限,这对于手机爱好者而言,这怎么可以忍?所以无论Android升到什么什么版 ...
- 分布式事物 - 基于RPC调用 - TCC模式
前提 前端业务(主服务)可以以同步或异步调用TCC框架,或者TCC框架本身就是同步异步兼备的. 假定TCC框架拥有断电后的自动恢复能力.同时,在下游业务出现无限失败的情况下,也会进行无限的重试,以达到 ...
- JS基础语法---函数作为返回值使用
结论:函数是可以作为返回值使用的 function f1() { console.log("f1函数调用了"); return function() { console.log(& ...
- 如何在windows下安装linux双系统
首先是看这篇博客,讲得很详细,但是有一点小小的区别,这里把整个过程回顾一下. https://www.cnblogs.com/masbay/p/10745170.html 第一步,刻盘,将一个u盘刻录 ...
- 【原创】你的Redis怎么持久化的
引言 (本文改编自生活真实案例,如有类同,绝不是巧合!) 端午节,烟哥正在一边愉快的学习.... 突然,微信一阵抖动.原来是老刘呼唤烟哥!善良的烟哥本以为人家是要约我出去玩!然而,打开微信一看,出现下 ...