Я разработал медиасервер VoIP, который обменивается RTP-пакетами с удаленными конечными точками SIP.Он должен хорошо масштабироваться - и хотя я изначально был обеспокоен тем, что моя реализация C # не будет приближаться к версии C ++, которую он заменяет, я использовал различные профилировщики, чтобы отточить реализацию, и производительность довольно близка.
У меня естьустранение большинства распределений объектов путем создания пулов многократно используемых объектов, я использую ReceiveFromAsync и SendToAsync для отправки / получения дейтаграмм и использую очереди производителя / потребителя для передачи пакетов RTP по системе.На машине с 2 процессорами Xeon 2,4 ГГц теперь я могу обрабатывать около 1000 одновременных потоков, каждый из которых отправляет / получает 50 пакетов в секунду.Однако итеративный профиль / tweak / profile меня зацепил - и я уверен, что где-то там больше эффективности!
Событием, запускающим обработку, является делегат Completed, вызываемый на SocketAsyncEventArgs - который, в свою очередь, отправляетпакеты RTP через конвейер обработки.
Остается разочарование тем, что в пуле потоков IOCP, по-видимому, имеются значительные издержки.Профилировщик показывает, что только 72% времени Inclusive Sample занято в «моем коде» - время, предшествующее этому, кажется накладным на пул потоков (кадры стека ниже).
Итак, мои вопросы:
- Я что-то упустил в моем понимании?
- Можно ли уменьшить эти издержки?
- Можно ли заменить пул потоков, используемый асинхронными функциями сокетов, на использование пользовательских,легкий пул потоков с меньшими накладными расходами?
100% MediaGateway
95.35% Thread::intermediateThreadProc(void *)
88.37% ThreadNative::SetDomainLocalStore(class Object *)
88.37% BindIoCompletionCallbackStub(unsigned long,unsigned long,struct _OVERLAPPED *)
86.05% BindIoCompletionCallbackStubEx(unsigned long,unsigned long,struct _OVERLAPPED *,int)
86.05% ManagedThreadBase::ThreadPool(struct ADID,void (*)(void *),void *)
86.05% CrstBase::Enter(void)
86.05% AppDomainStack::PushDomain(struct ADID)
86.05% Thread::ShouldChangeAbortToUnload(class Frame *,class Frame *)
86.05% AppDomainStack::ClearDomainStack(void)
83.72% ThreadPoolNative::CorWaitHandleCleanupNative(void *)
83.72% __CT??_R0PAVEEArgumentException@@@84
83.72% DispatchCallDebuggerWrapper(unsigned long *,unsigned long,unsigned long *,unsigned
__int64,void *,unsigned __int64,unsigned int,unsigned char *,class 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,valuetype System.Threading.NativeOverlapped*)
76.74% System.Net.Sockets.SocketAsyncEventArgs.CompletionPortCallback(uint32,uint32,valuetype System.Threading.NativeOverlapped*)
76.74% System.Net.Sockets.SocketAsyncEventArgs.FinishOperationSuccess(valuetype System.Net.Sockets.SocketError,int32,valuetype System.Net.Sockets.SocketFlags)
74.42% System.Threading.ExecutionContext.Run(class System.Threading.ExecutionContext,class System.Threading.ContextCallback,object)
72.09% System.Net.Sockets.SocketAsyncEventArgs.ExecutionCallback(object)
72.09% System.Net.Sockets.SocketAsyncEventArgs.OnCompleted(class System.Net.Sockets.SocketAsyncEventArgs)