网络发包的优化

网络发送数据包,现成的接口Socket.Send在我的电脑上大概会有700us左右的耗时,如果使用SendAsync开销会更大,并且会导致数据包的乱序。
Send阻塞接口慢的原因一方面底层会跑很多代码,另一方面有线程就有竞争,有竞争就有加锁。
于是写了一个轻量的Queue来加速网络发包,平均耗时从70us降低到300ns。

public static void Main1()
{
    // create the socket
    Socket listenSocket = new Socket(AddressFamily.InterNetwork,
                                     SocketType.Stream,
                                     ProtocolType.Tcp);

    // bind the listening socket to the port
    IPAddress hostIP = IPAddress.Any;
    IPEndPoint endpoint = new IPEndPoint(hostIP, 10042);
    listenSocket.Bind(endpoint);

    // start listening
    listenSocket.Listen(2);

    var socket = listenSocket.Accept();
    ConfigureTcpSocket(socket);

    int count = 0;

    Stopwatch sw = new Stopwatch();

    for (int i = 0; i < 100; i++)
    {
        count++;

        sw.Restart();
        sw.Stop();

        Console.WriteLine($"{count} stopwatch empty body, time {sw.Elapsed.Ticks}");

    }

    SuperQueue<byte[]> superQueue = new SuperQueue<byte[]>(100);

    Task.Factory.StartNew(() =>
    {
        while (true)
        {
            if (superQueue.Count == 0)
            {
                Thread.Sleep(0);
                continue;
            }

            superQueue.TryDequeue(out byte[] data);
            var byteSent = socket.Send(data);

            Console.WriteLine($"{count} socket send {byteSent}");
            Console.Out.Flush();
        }
    });
    for (int i = 0; i < 100; i++)
    {
        count++;

        sw.Restart();
        superQueue.TryEnqueue(new byte[1024]);
        sw.Stop();

        Console.WriteLine($"{count} stopwatch superQueue, time {sw.Elapsed.Ticks}");

    }

    count = 0;
    for (int i = 0; i < 100; i++)
    {
        count++;

        sw.Restart();
        int nSent = socket.Send(new byte[1024]);
        sw.Stop();

        Console.WriteLine($"{count} socket send {nSent} bytes, time {sw.Elapsed.Ticks}");

    }

    count = 0;
    for (int i = 0; i < 100; i++)
    {
        int newCount = count++;

        sw.Restart();
        socket.SendAsync(new ArraySegment<byte>(new byte[1024]), SocketFlags.None).ContinueWith(task =>
        {
            Console.WriteLine($"{newCount} socket send {task.Result}");
        });
        sw.Stop();
        
        Console.WriteLine($"ASYNC: {newCount} socket send, time {sw.Elapsed.Ticks}");

    }


    Console.ReadLine();
}

public class SuperQueue<T>
    where T : class
    {
        private T[] array;
        private int head;       // The index from which to dequeue if the queue isn't empty.

        //tail只跟生产者有关
        private int tail;       // The index at which to enqueue if the queue isn't full.
        private int size;       // Number of elements.

        public int Count => size;

        public SuperQueue(int capacity)
        {
            if (capacity < 0)
                throw new ArgumentOutOfRangeException(nameof(capacity), capacity, "ArgumentOutOfRange_NeedNonNegNum");
            array = new T[capacity];
        }


        //生产者线程
        public bool TryEnqueue(T item)
        {
            if (size == array.Length)
            {
                return false;
            }

            array[tail] = item;
            MoveNext(ref tail);
            Interlocked.Increment(ref size);
            return true;
        }


        public bool TryDequeue(out T result)
        {
            if (size == 0)
            {
                result = default;
                return false;
            }

            result = array[head];
            if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
            {
                array[head] = default;
            }
            MoveNext(ref head);
            Interlocked.Decrement(ref size);
            return true;
        }


        // Increments the index wrapping it if necessary.
        private void MoveNext(ref int index)
        {
            // It is tempting to use the remainder operator here but it is actually much slower
            // than a simple comparison and a rarely taken branch.
            // JIT produces better code than with ternary operator ?:
            int tmp = index + 1;
            if (tmp == array.Length)
            {
                tmp = 0;
            }
            index = tmp;
        }

    }
posted @ 2022-11-18 15:14  dewxin  阅读(75)  评论(0编辑  收藏  举报