首页 > 解决方案 > C# byte [] 操作 - 我应该使用 ArrayPool 还是 MemoryPool?

问题描述

我正在编写一个服务器应用程序。它接受数千个传入的套接字连接,每个连接都发送和接收消息。

每次通过套接字接收或发送消息时,我都会分配新的byte[] 缓冲区,然后它们会被垃圾收集:

byte[] receivingBuffer = new byte[bufferSize];

为了提高性能,我想重用byte[] buffers。我应该使用ArrayPool还是MemoryPool

或者我应该创建一个固定长度的 byte[] 缓冲区的ObjectPool吗?例如,如果我发送和接收的消息从不超过 100 KB,那么我创建一个100 KB byte[] 缓冲区的ObjectPool。每次我需要发送或接收消息时,我都会得到其中一个缓冲区。

标签: socketsmemory-poolarraypools

解决方案


做了一些研究和测试。ArrayPool 是正确使用的东西,它将显着提高性能。

class Program
{
    private static ManualResetEvent m_event = new ManualResetEvent(false);
    private static DateTime m_dtStart;
    private static int m_iNumOfIterations = 0;
    private static int m_iNumOfExceptions = 0;

    private static double m_dAvgSizeRatio = 0;
    private static object m_lock = new object();

    private static void ReportSizeRatio(double dRatio)
    {
        lock (m_lock)
        {
            m_dAvgSizeRatio = (m_dAvgSizeRatio * m_iNumOfIterations + dRatio) / (m_iNumOfIterations + 1);
            m_iNumOfIterations++;
        }
    }

    /// <summary>
    /// When using ArrayPool: 
    /// - CPU:    1 ~ 1.5%
    /// - Memory: 67 MB
    /// - Speed:  9130 runs/sec
    /// - Given buffer is 1.56 times bigger than asked for.
    /// 
    /// When NOT using ArrayPool: 
    /// - CPU:    20 ~ 25%
    /// - Memory: 500 ~ 1000 MB
    /// - Speed:  5300 runs/sec
    /// 
    /// Conclusion: huge improvement in performance.
    /// </summary>
    /// <param name="obj"></param>
    private static void Test(object obj)
    {
        TrueRandom random = new TrueRandom(500, 800);
        m_event.WaitOne();

        while (true)
        {
            int iDesiredSize = random.GetRandomInteger() * 1000;
            byte[] buffer = null;

            try
            {
                //buffer = ArrayPool<byte>.Shared.Rent(iDesiredSize);
                buffer = new byte[iDesiredSize];

                ReportSizeRatio((double)buffer.Length / (double)iDesiredSize);
            }
            catch
            {
                Interlocked.Increment(ref m_iNumOfExceptions);
            }

            Thread.Sleep(100);
            //ArrayPool<byte>.Shared.Return(buffer);
        }
    }


    static void Main(string[] args)
    {

        for (int i = 0; i < 1000; i++)
        {
            Thread thread = new Thread(Test);
            thread.Start();
        }

        Console.WriteLine("All threads fired.");
        m_dtStart = DateTime.Now;
        m_event.Set();

        while (true)
        {
            Thread.Sleep(1000);
            Console.Clear();
            Console.WriteLine($"Iterations/sec: { (int)(m_iNumOfIterations / (DateTime.Now - m_dtStart).TotalSeconds) }, Ave rented size: { (m_dAvgSizeRatio * 100d).ToString("0.0") }%, exceptions: { m_iNumOfExceptions }.");
        }
    }
}

推荐阅读