Para um número muito pequeno de soquetes (varia dependendo do seu hardware, é claro, mas estamos falando de algo na ordem de 10 ou menos), o select pode superar o epoll em uso de memória e velocidade de execução. É claro que, para um número tão pequeno de soquetes, os dois mecanismos são tão rápidos que você realmente não se importa com essa diferença na grande maioria dos casos.
Porém, um esclarecimento. Ambos selecionam e dimensionam linearmente. Uma grande diferença, porém, é que as APIs voltadas para o espaço do usuário têm complexidades baseadas em coisas diferentes. O custo de uma select
chamada é aproximadamente igual ao valor do descritor de arquivo de maior numeração que você passa. Se você selecionar em um único fd, 100, isso é quase duas vezes mais caro do que selecionar em um único fd, 50. Adicionar mais fds abaixo do mais alto não é totalmente gratuito, então é um pouco mais complicado do que isso na prática, mas isso é uma boa primeira aproximação para a maioria das implementações.
O custo do epoll está mais próximo do número de descritores de arquivo que realmente contêm eventos. Se você está monitorando 200 descritores de arquivo, mas apenas 100 deles têm eventos neles, então você (quase) está pagando apenas por esses 100 descritores de arquivo ativos. É aqui que epoll tende a oferecer uma de suas principais vantagens em relação ao select. Se você tiver mil clientes que estão em sua maioria ociosos, quando usar select, ainda estará pagando por todos os mil deles. No entanto, com epoll, é como se você tivesse apenas alguns - você só está pagando por aqueles que estão ativos em um determinado momento.
Tudo isso significa que o epoll levará a menos uso da CPU para a maioria das cargas de trabalho. No que diz respeito ao uso de memória, é um pouco complicado. select
consegue representar todas as informações necessárias de uma forma altamente compacta (um bit por descritor de arquivo). E a limitação FD_SETSIZE (normalmente 1024) em quantos descritores de arquivo você pode usar select
significa que você nunca vai gastar mais de 128 bytes para cada um dos três conjuntos de fd que você pode usar comselect
(ler, escrever, exceção). Comparado com aqueles 384 bytes máximos, epoll é uma espécie de porco. Cada descritor de arquivo é representado por uma estrutura multibyte. Porém, em termos absolutos, ainda não vai usar muita memória. Você pode representar um grande número de descritores de arquivo em algumas dezenas de kilobytes (cerca de 20k por 1000 descritores de arquivo, eu acho). E você também pode acrescentar o fato de que terá de gastar todos os 384 desses bytes com select
se quiser monitorar apenas um descritor de arquivo, mas seu valor for 1024, enquanto com epoll você gastaria apenas 20 bytes. Ainda assim, todos esses números são muito pequenos, então não faz muita diferença.
E há também aquele outro benefício do epoll, que talvez você já conheça, que não se limita aos descritores de arquivo FD_SETSIZE. Você pode usá-lo para monitorar quantos descritores de arquivo você tiver. E se você tiver apenas um descritor de arquivo, mas seu valor for maior que FD_SETSIZE, epoll funciona com ele também, mas select
não funciona.
Aleatoriamente, também descobri recentemente uma ligeira desvantagem de epoll
em comparação com select
ou poll
. Embora nenhuma dessas três APIs ofereça suporte a arquivos normais (ou seja, arquivos em um sistema de arquivos), select
e poll
apresente essa falta de suporte ao relatar tais descritores como sempre legíveis e sempre graváveis. Isso os torna inadequados para qualquer tipo significativo de I / O sem bloqueio de sistema de arquivos, um programa que usa select
ou poll
e acontece de encontrar um descritor de arquivo do sistema de arquivos pelo menos continuará a operar (ou se falhar, não será porque de select
ou poll
), embora talvez não com o melhor desempenho.
Por outro lado, epoll
falhará rapidamente com um erro ( EPERM
aparentemente) quando for solicitado a monitorar esse descritor de arquivo. A rigor, isso dificilmente é incorreto. Ele está apenas sinalizando sua falta de suporte de forma explícita. Normalmente, eu aplaudiria as condições de falha explícitas, mas esta não é documentada (pelo que posso dizer) e resulta em um aplicativo completamente quebrado, ao invés de um que simplesmente opera com desempenho potencialmente degradado.
Na prática, o único lugar em que vi isso acontecer foi ao interagir com o stdio. Um usuário pode redirecionar stdin ou stdout de / para um arquivo normal. Considerando que anteriormente stdin e stdout teriam sido um pipe - suportado por epoll perfeitamente - ele se torna um arquivo normal e epoll falha ruidosamente, quebrando o aplicativo.
poll
para integridade?