C# 我使用disruptor net的代码比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应该比BlockingCollection快得多

在我的上一个问题中,我写了两个测试
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