[Learning accumulation] Queue and ConcurrentQueue performance test

        In C#, there are two kinds of queues (Queue), one is the queue we usually use, and the other is the thread-safe queue ConcurrentQueue<T>.

ConcurrentQueue represents a thread-safe first-in-first-out (FIFO) collection. https://learn.microsoft.com/zh-cn/dotnet/api/system.collections.concurrent.concurrentqueue-1?view=netstandard-2.1         Both of them are first-in-first-out (FIFO) in terms of data structure Collection, in general, we use Queue, which is a regular queue, which can meet the demand. Of course, in the case of multi-threading, if Queue is used, because threads are not safe, exceptions will be caused when threads compete (multi-threads enter or exit the queue), and we will use ConcurrentQueue at this time. So what is the performance difference between the two?

        This article will conduct a simple performance test on these two queues, and discuss a special case: the use of Queue when one thread enters the queue and one thread exits the queue.

1. Single thread enqueue + single thread dequeue

        Here we discuss the special case of multi-threading: Assume that the queue has only one thread to enqueue, and at the same time, only one thread to dequeue. So in this case, will there be any problem if we use the thread-unsafe queue Queue? If writing and reading are time-consuming operations in Unity, will there be an exception?

        Here our test case is simple:

        public static void AddTaskItem()
        {
            System.Random rand = new System.Random();

            for (int i = 0; i < MaxCount; i++)
            {
                TaskItem item = new TaskItem();
                item.Index = i;
                TestQueue.Enqueue(item);
                Thread.Sleep(rand.Next(0, 5));
            }

            Debug.Log("全部数据添加完毕!");
        }


        public class TaskItem
        {
            public int Index;
    
            public void DoSomeWork(){}// 某耗时函数,此处略去
        }

        Here I set MaxCount to 10000, and then update on the main thread of Unity:

        public int CurIndex = 0;

        private void Update()
        {
            int updateCount = TestQueue.Count;
            int lastIndex = CurIndex;
            //取出当前队列的所有值,并比对;
            while (testQueue.Count > 0)
            {
                var item = testQueue.Dequeue();
                if (item.Index != CurIndex)
                {
                    Debug.LogError($"取值错误,应该是:{CurIndex},实际是  :{item.Index}");
                }

                item.DoSomeWork();

                CurIndex++;
            }

            Debug.Log($"本次取出队列:{CurIndex - lastIndex} / {updateCount}");
        }

        Obviously, as long as the value is wrong, that is, the currently retrieved value is not the serial number (CurIndex) recorded in the main thread, an error will be thrown. However, I have conducted many tests, and there is no error. Even if the time-consuming function is added, the value of each frame is not the original value, but the exception of dequeue and enqueue will still not appear.

        So we conclude:

        When only one thread is enqueued and one thread is dequeued, there will be no exception when using the queue Queue.

2. Performance test

       Here we simply conduct performance tests for entering and exiting the team. We will not post the test code here, and draw conclusions directly:

        Overall, the performance overhead of ConcurrentQueue is greater than that of Queue, where:

        Writing time: ConcurrentQueue is about 1.3 times that of Queue.

        Reading time: ConcurrentQueue is about 6 times that of Queue.

        At the same time, as the data in the queue increases, the reading time of ConcurrentQueue will increase significantly.

        Of course, if there are not many queues, the difference between the two is not too big, and the difference at the microsecond level can be ignored under normal circumstances. At the same time, it is very rare in general games that there are tens of millions of elements in the queue (generally, a few hundred elements in the queue are terrible), so don't pay too much attention to the additional performance overhead caused by ConcurrentQueue.

        At the same time, this test is all single-threaded reading and writing, which is equivalent to abandoning the advantages of ConcurrentQueue (multi-threaded safety) to test, which is a bit unfair. In actual use, ConcurrentQueue must be in a multi-threaded read and write scenario, and its security and performance will definitely be significantly better than Queue.

Guess you like

Origin blog.csdn.net/cyf649669121/article/details/130162599