C# 我使用disruptor net的代码比BlockingCollection慢
Disruptor应该比BlockingCollection快得多 在我的上一个问题中,我写了两个测试C# 我使用disruptor net的代码比BlockingCollection慢,c#,disruptor-pattern,C#,Disruptor Pattern,Disruptor应该比BlockingCollection快得多 在我的上一个问题中,我写了两个测试Disruptor花费约1微秒(或更少),而BlockingCollection花费约14微秒 所以我决定在我的程序中使用Disruptor,但当我实现它时,我发现现在Disruptor花费了大约50微秒,而BlockingCollection仍然花费14-18微秒 我已经将我的生产代码修改为“独立测试”,而Disruptor仍然花费50微秒。为什么? 下面是一个简化的测试。在这个测试中,我有两
Disruptor花费约1微秒(或更少),而BlockingCollection花费约14微秒
所以我决定在我的程序中使用Disruptor
,但当我实现它时,我发现现在Disruptor
花费了大约50
微秒,而BlockingCollection仍然花费14-18
微秒
我已经将我的生产代码修改为“独立测试”,而Disruptor
仍然花费50微秒。为什么?
下面是一个简化的测试。在这个测试中,我有两个选择。第一个选项是睡眠1ms
。然后,Disruptor
花费30-50微秒来交付。第二个选项是模拟活动。然后,Disruptor
花费7微秒来交付。使用BlockingCollection
进行相同的测试,结果为14-18微秒。那么为什么Disruptor不比BlockingCollection快呢
在我的实际应用程序中,Disruptor
花费50微秒来交付太多的东西!我认为它传递信息的速度应该比1微秒快得多
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using Disruptor;
namespace DisruptorTest
{
public sealed class ValueEntry
{
internal int Id { get; set; }
}
class DisruptorTest
{
public class MyHandler : IEventHandler<ValueEntry>
{
private DisruptorTest _parent;
public MyHandler(DisruptorTest parent)
{
this._parent = parent;
}
public void OnNext(ValueEntry data, long sequence, bool endOfBatch)
{
_parent.sw.Stop();
long microseconds = _parent.sw.ElapsedTicks / (Stopwatch.Frequency / (1000L * 1000L));
// Filter out abnormal delays > 1000
if (microseconds < 1000)
{
_parent.sum += (int)microseconds;
_parent.count++;
if (_parent.count % 1000 == 0)
{
Console.WriteLine("average disruptor delay (microseconds) = {0}", _parent.sum / _parent.count);
}
}
}
}
private RingBuffer<ValueEntry> _ringBuffer;
private const int RingSize = 64;
static void Main(string[] args)
{
new DisruptorTest().Run();
}
public void Run()
{
var disruptor = new Disruptor.Dsl.Disruptor<ValueEntry>(() => new ValueEntry(), RingSize, TaskScheduler.Default);
disruptor.HandleEventsWith(new MyHandler(this));
_ringBuffer = disruptor.Start();
for (int i = 0; i < 10001; i++)
{
Do();
// We need to simulate activity to allow event to deliver
// Option1. just Sleep. Result 30-50 microseconds.
Thread.Sleep(1);
// Option2. Do something. Result ~7 microseconds.
//factorial = 1;
//for (int j = 1; j < 100000; j++)
//{
// factorial *= j;
//}
}
}
public static int factorial;
private Stopwatch sw = Stopwatch.StartNew();
private int sum;
private int count;
public void Do()
{
long sequenceNo = _ringBuffer.Next();
_ringBuffer[sequenceNo].Id = 0;
sw.Restart();
_ringBuffer.Publish(sequenceNo);
}
}
}
您仍然在做同样的事情:您正在测量发布单个项目所需的时间
public void Do(int id)
{
long sequenceNo = _ringBuffer.Next();
_ringBuffer[sequenceNo].Id = id;
sw[id].Restart(); // <--- You're doing this EVERY TIME YOU PUBLISH an item!
_ringBuffer.Publish(sequenceNo);
}
清理你的代码
至少,你应该试着清理一下你的代码;我对它进行了一些重新组织,这样它就不会那么凌乱了:
disputer一开始并不是那么简单,现在我们必须处理您的代码并尝试告诉您如何修复它。更重要的是:当你有意大利面代码时,你不能进行性能优化或测试。尽量保持一切简单和干净。在这个阶段,您的代码既不简单也不干净
让我们从私人成员变量的可怕命名约定开始:
private const int NumberOfThreads = 1;
private RingBuffer<ValueEntry> _ringBuffer;
private const int RingSize = 64;
private const int MaxId = 100
private Stopwatch[] sw = new Stopwatch[MaxId];
private int[] sum = new int[MaxId];
private int[] count = new int[MaxId];
在我的机器上,分辨率在320纳秒以内。因此OP是正确的,定时器的分辨率不应该是问题
我知道OP想要衡量一件物品的平均交付量,但有(至少)两种方法可以做到这一点
我们必须调查差异。在概念层面上,您正按照下面的代码进行操作:
您正在运行一系列迭代
测量每一个
你计算总数
最后计算平均值
代码:
Stopwatch sw = new Stopwatch();
long totalMicroseconds = 0;
int numItems = 1000;
for(int i = 0; i < numItems; i++)
{
sw.Reset();
sw.Start();
OneItemDelivery();
sw.Stop();
totalMicroseconds += sw.ElapsedTicks / (Stopwatch.Frequency / (1000L * 1000L));
}
long avgOneItemDelivery = totalMicroseconds/numItems;
sw.Start();
for(int i = 0; i < numItems; i++)
{
OneItemDelivery();
}
sw.Stop();
totalMicroseconds = sw.ElapsedTicks / (Stopwatch.Frequency / (1000L * 1000L));
long avgOneItemDelivery = totalMicroseconds/numItems;
Stopwatch sw=新秒表();
长总微秒=0;
int numItems=1000;
对于(int i=0;i
衡量绩效的另一种方法是:
启动计时器
运行所有的迭代
停止计时
计算平均时间
代码:
Stopwatch sw = new Stopwatch();
long totalMicroseconds = 0;
int numItems = 1000;
for(int i = 0; i < numItems; i++)
{
sw.Reset();
sw.Start();
OneItemDelivery();
sw.Stop();
totalMicroseconds += sw.ElapsedTicks / (Stopwatch.Frequency / (1000L * 1000L));
}
long avgOneItemDelivery = totalMicroseconds/numItems;
sw.Start();
for(int i = 0; i < numItems; i++)
{
OneItemDelivery();
}
sw.Stop();
totalMicroseconds = sw.ElapsedTicks / (Stopwatch.Frequency / (1000L * 1000L));
long avgOneItemDelivery = totalMicroseconds/numItems;
sw.Start();
对于(int i=0;i
每个人都有自己的问题:
- 第一种方法可能精度较低,您需要在您的系统上证明秒表可以精确地处理少量的工作(不仅仅是计算纳秒精度)
- 第二种方法还将包括迭代发生所需的计算时间。这会在测量中引入少量偏差,但它会抵消第一种方法通常会遇到的精度问题
您已经注意到,Sleep
语句会产生较低的性能,因此我建议您进行简单的计算。计算阶乘似乎是个好主意,只需做一个非常小的计算:不需要100000,100也可以
当然,您不需要等待2分钟进行测试,但10-20秒应该不是问题。您仍然在做同样的事情:您正在测量发布单个项目所需的时间
public void Do(int id)
{
long sequenceNo = _ringBuffer.Next();
_ringBuffer[sequenceNo].Id = id;
sw[id].Restart(); // <--- You're doing this EVERY TIME YOU PUBLISH an item!
_ringBuffer.Publish(sequenceNo);
}
清理你的代码
至少,你应该试着清理一下你的代码;我对它进行了一些重新组织,这样它就不会那么凌乱了:
disputer一开始并不是那么简单,现在我们必须处理您的代码并尝试告诉您如何修复它。更重要的是:当你有意大利面代码时,你不能进行性能优化或测试。尽量保持一切简单和干净。在这个阶段,您的代码既不简单也不干净
让我们从私人成员变量的可怕命名约定开始:
private const int NumberOfThreads = 1;
private RingBuffer<ValueEntry> _ringBuffer;
private const int RingSize = 64;
private const int MaxId = 100
private Stopwatch[] sw = new Stopwatch[MaxId];
private int[] sum = new int[MaxId];
private int[] count = new int[MaxId];
在我的机器上,分辨率在320纳秒以内。因此OP是正确的,定时器的分辨率不应该是问题
我知道OP想要衡量一件物品的平均交付量,但有(至少)两种方法可以做到这一点
我们必须调查差异。在概念层面上,您正按照下面的代码进行操作:
您正在运行一系列迭代
测量每一个
你计算总数
最后计算平均值
代码:
Stopwatch sw = new Stopwatch();
long totalMicroseconds = 0;
int numItems = 1000;
for(int i = 0; i < numItems; i++)
{
sw.Reset();
sw.Start();
OneItemDelivery();
sw.Stop();
totalMicroseconds += sw.ElapsedTicks / (Stopwatch.Frequency / (1000L * 1000L));
}
long avgOneItemDelivery = totalMicroseconds/numItems;
sw.Start();
for(int i = 0; i < numItems; i++)
{
OneItemDelivery();
}
sw.Stop();
totalMicroseconds = sw.ElapsedTicks / (Stopwatch.Frequency / (1000L * 1000L));
long avgOneItemDelivery = totalMicroseconds/numItems;
Stopwatch sw=新秒表();
长总微秒=0;
int numItems=1000;
对于(int i=0;i
衡量绩效的另一种方法是:
启动计时器
运行所有的迭代
停止计时
计算平均时间
代码:
Stopwatch sw = new Stopwatch();
long totalMicroseconds = 0;
int numItems = 1000;
for(int i = 0; i < numItems; i++)
{
sw.Reset();
sw.Start();
OneItemDelivery();
sw.Stop();
totalMicroseconds += sw.ElapsedTicks / (Stopwatch.Frequency / (1000L * 1000L));
}
long avgOneItemDelivery = totalMicroseconds/numItems;
sw.Start();
for(int i = 0; i < numItems; i++)
{
OneItemDelivery();
}
sw.Stop();
totalMicroseconds = sw.ElapsedTicks / (Stopwatch.Frequency / (1000L * 1000L));
long avgOneItemDelivery = totalMicroseconds/numItems;
sw.Start();
对于(int i=0;i