Я работаю на сервере, который получает запросы от другого сервера, вычисляет значение и возвращает результат по всему TCP.
Я совсем новичок в разработке бэкэнда. В прошлом месяце, с 20000 ответов в секунду до 80000, я многому научился и применил их к этому серверу: предварительное выделение буфера byte [], вызовы API non-alloc, поток данных TPL, Threading.
В основномвся обработка теперь перемещается в конвейер обработки, сеть, принимающая / отправляющая, находится в отдельном потоке.
Однако общая производительность по-прежнему ограничена сетевым вводом-выводом, который реализован с помощью TCPClient.
Реализация
- Используемый метод NetworkStream R / W является блокирующим. Сначала прочитайте 4 байта, чтобы получить длину, 2 байта для заголовка, затем N байтов для тела сообщения.
- Я использую объект обертки буфера byte [], названный BufferSegmentPointer . Эти объекты предварительно инициализируются с помощью байта [] определенной длины (> 2048).
- BufferSegmentPointer передаются по конвейеру потока данных TPL, после обработки запроса результатом является буфер.BlockCopy () в буфер, в него заносится используемый диапазон byte [], затем он отправляется в NET OUTPUT через конвейер, поэтому NetworkStream.Write () просто знает, что писать.
- Сегмент обработки конвейера представляет собой TransformBlock с MaxDegreeOfParallism, установленным в Enviromnet.ProcessorCount - 2.
Тестовая статистика
Программа представляет собой просто конвейер с IN > ОБРАБОТКА > OUT .
, которые можно разбить на 5 сегментов:
NET IN > INПередача > ОБРАБОТКА > Выход Передача > NET OUT
Отмечено, что следующий результат теста ВМ ограничен из-зак тестовому клиенту на ВМ не может отправить достаточно запросов. Это мое узкое место. Поток ввода-вывода NetworkStream слишком медленный.
Заметил, что если я объединю десятки запросов в один вызов NetworkStream.Write (), он будет работать заметно лучше. , но это бессмысленно, потому что запросы никогда не связываются в реальном производственном сценарии.
Заметил, что если я просто напишу байт 2 ГБ [] из тестового клиента и получу его с сервера, это будет стоить 0,8 секунды. (Все тесты, упомянутые в этом посте, выполняются на локальном хосте.)
Тест 1: TCP I
1000000 Запрос 702 байта, отправленный тестовым клиентом вцикл.
Секундомер запускается при первом получении, останавливается при получении всех.
O> X> X> X> X (не передается через поток данных, считается после получения)
Результат
- 12.99 с на ESXi VM 2,4 ГГц * 12
- 5,86 с на i5-7500 3,4 ГГц * 4
Тест 2: TCP O
Для проверки пропускной способности записи.
1000000 Вызов NetworkStream.Write ()из N байт.
Секундомер запускается непосредственно перед первой отправкой, останавливается при отправке всех.
X> X> X> X> O
Результат
- 8,42 с N = 653 на ESXi VM 2,4 ГГц * 12
- 4,75 с N = 577, вклi5-7500 3,4 ГГц * 4
Тест 3: TCP IO
1000000 Запрос 702 байта, отправленный тестовым клиентом в цикле.
1000000 ответ, 500 ~ 700 байт, поскольку используется случайный результат.
Секундомер запускается непосредственно перед первым запросом, отправленным тестовым клиентом, останавливается при получении всех ответов.
O> X> X> X> O
(Запрос не передан черезПоток данных, считается после получения)
(Ответ создан и преобразован в байт [] заранее, просто запишите его один раз при получении запроса)
Результат
- 11,7 с На ESXi VM 2,4 ГГц * 12
- 5,74 на i5-7500 3,4 ГГц * 4
Тест 4: Локальная обработка
1000000 запрос 702 байта, сгенерированный сервером сам.
Нет ответов, после выполнения вызова обработки счетчик ++.
Секундомер запускается непосредственно перед подачей первого запроса в конвейер, останавливается, когда обработчик достигает 1000000.
X> O> O> X> X
Результат
- 6,89 с на ESXi VM 2,4 ГГц * 12
- 8,28 с на i5-7500 3,4 ГГц *4
Тест 5: Производственный тест
Обработка включена. 1000000 запросов, 702 байта, отправленных тестовым клиентом в цикле.
1000000 ответ, 500 ~ 700 байт, поскольку используется случайный результат.
Секундомер запускается непосредственно перед первым запросом, отправленным тестовым клиентом, останавливается, когда все ответы получены.
O> O> O> O> O
Результат
- 18,6 с на ESXi VM 2,4 ГГц * 12
- 11,8 с на i5-7500 3,4 ГГц *4
Мои вопросы
- Это нормальная пропускная способность <100 Мбит / с? </li>
- Могут ли помочь неблокирующие (асинхронные) вызовы?
- Возможно ли, что SAEA (SocketAsyncEventArgs) сможет побить TCPClient? Я видел, что методы сокетов будут пытаться использовать SAEA, если это возможно, и TCPClient - просто оболочка ......
- Есть ли у вас предположение, что может быть узким местом?
- Мой супервизор может отправить более 300000 / с запросов (> 200 Мбит / с) с JAVA, действительно ли это JAVA превосходит производительность .NET, или это глупость?
- Благодаря тому, что NetworkStream R / W работает лучшена моем офисном ПК (i5) по сравнению с виртуальной машиной я считаю, что NetworkStream ограничена производительностью одного ядра (потока) (верно ли это предположение?), но мне нужно знать, почему это так медленно? Это должно быть так медленно?