Затраты.NET IOCP ThreadPool с асинхронными операциями UDP
Я разработал медиа-сервер VoIP, который обменивается RTP-пакетами с удаленными конечными точками SIP. Он должен хорошо масштабироваться - и хотя я изначально был обеспокоен тем, что моя реализация C# не будет приближаться к версии C++, которую она заменяет, я использовал различные профилировщики, чтобы отточить реализацию, и производительность довольно близка.
Я исключил большинство распределений объектов, создав пулы многократно используемых объектов, я использую ReceiveFromAsync и SendToAsync для отправки / получения дейтаграмм, и я использую очереди производителя / потребителя для передачи пакетов RTP по системе. На машине с 2 процессорами Xeon 2,4 ГГц теперь я могу обрабатывать около 1000 одновременных потоков, каждый из которых отправляет / получает 50 пакетов в секунду. Тем не менее, итеративный профиль / настройка / профиль меня зацепил - и я уверен, что где-то там больше эффективности!
Событием, запускающим обработку, является делегат Completed, вызываемый для SocketAsyncEventArgs, который, в свою очередь, отправляет пакеты RTP через конвейер обработки.
Остальное разочарование заключается в том, что в пуле потоков IOCP, по-видимому, имеются значительные издержки. Профилировщик показывает, что только 72% времени Inclusive Sample приходится на "мой код" - время до этого, по-видимому, связано с накладными расходами пула потоков (стеки кадров ниже).
Итак, мои вопросы:
- Я что-то упустил в моем понимании?
- Можно ли уменьшить эти накладные расходы?
- Можно ли заменить пул потоков, используемый функциями асинхронного сокета, на пользовательский, легкий пул потоков с меньшими издержками?
100% MediaGateway 95,35% Тема:: промежуточный_процесс (пустота *) 88,37% ThreadNative::SetDomainLocalStore(класс Object *) 88,37% BindIoCompletionCallbackStub(длинная без знака, длинная без знака, структура _OVERLAPPED *) 86.05% BindIoCompletionCallbackStubEx(длинная без знака, длинная без знака, структура _OVERLAPPED *,int) 86,05% ManagedThreadBase::ThreadPool(структура ADID,void (*)(void *),void *) 86,05% CrstBase::Enter(пусто) 86,05% AppDomainStack::PushDomain(структура ADID) 86.05% Thread::ShouldChangeAbortToUnload(класс Frame *, класс Frame *) 86,05% AppDomainStack::ClearDomainStack(пусто) 83,72% ThreadPoolNative::CorWaitHandleCleanupNative(void *) 83,72% __CT??_R0PAVEEArgumentException@@@84 83.72% DispatchCallDebuggerWrapper(длинная без знака *, длинная без знака, длинная без знака *, без знака __int64,void *,unsigned __int64,unsigned int,unsigned char *, класс ContextTransitionFrame *) 83.72% DispatchCallBody(unsigned long *,unsigned long,unsigned long *,unsigned __int64,void *,unsigned __int64,unsigned int,unsigned char *) 83,72% MethodDesc::EnsureActive(void) 81,40% _CallDescrWorker@20 81,40% System.Threading._IOCompletionCallback.PerformIOCompletionCallback(uint32,uint32, значение типа System.Threading.NativeOverlapped*) 76,74% System.Net.Sockets.SocketAsyncEventArgs.CompletionPortCallback(uint32,uint32, значение типа System.Threading.NativeOverlapped*) 76,74% System.Net.Sockets.SocketAsyncEventArgs.FinishOperationSuccess(значение типа System.Net.Sockets.SocketError,int32, значение типа System.Net.Sockets.SocketFlags) 74,42% System.Threading.ExecutionContext.Run(класс System.Threading.ExecutionContext, класс System.Threading.ContextCallback, объект) 72,09% System.Net.Sockets.SocketAsyncEventArgs.ExecutionCallback(объект) 72,09% System.Net.Sockets.SocketAsyncEventArgs.OnCompleted(класс System.Net.Sockets.SocketAsyncEventArgs)
2 ответа
50 000 пакетов в секунду в Windows - это неплохо, я бы сказал, что аппаратное обеспечение и операционная система - более важные проблемы для масштабирования. Разные сетевые интерфейсы накладывают разные ограничения: сетевые адаптеры Intel имеют преимущественно высокую производительность с хорошими кроссплатформенными драйверами, однако Broadcom не имеют хороших результатов в Windows по сравнению с Linux. Расширенные API-интерфейсы ядра сети Windows доступны только в том случае, если драйверы поддерживают эти функции, и Broadcom доказала, что является компанией, которая включает расширенные функции только для более нового оборудования, несмотря на поддержку более старых устройств из других операционных систем.
Я бы начал исследовать несколько сетевых адаптеров, например, с четырехядерной серверной сетевой платой Intel и использовать расширенные сетевые API-интерфейсы Windows для привязки одного сетевого адаптера к каждому ядру обработки. Теоретически вы можете отправить 50 000 через один сетевой адаптер и 50 000 через другой.
http://msdn.microsoft.com/en-us/library/ff568337(v=VS.85).aspx
Однако, похоже, что у вас нет базовых показателей для оценки эффективности кода. Я ожидаю увидеть сравнение с серверами, не использующими полезную нагрузку VoIP, работающими на транспорте TCP вместо UDP и работающими в других операционных системах для сравнения стека IP и эффективности API.
Просто добавьте немного информации - я недавно обнаружил, что в пуле потоков IOCP присутствует ошибка, которая может повлиять на вашу производительность: см. Пункт 3 раздела "причина" в http://support.microsoft.com/kb/2538826. Это может быть действительно для вашего случая.