Receio estar "fazendo a coisa errada" aqui, se assim for, me exclua e peço desculpas. Em particular, não vejo como crio as pequenas anotações que algumas pessoas criaram. No entanto, tenho muitas preocupações / observações a fazer sobre este tópico.
1) O elemento comentado no pseudo-código em uma das respostas populares
result = query( "select smurfs from some_mushroom" );
// twiddle fingers
go_do_something_with_result( result );
é essencialmente falso. Se o encadeamento estiver computando, então não está girando o polegar, está fazendo o trabalho necessário. Se, por outro lado, é simplesmente aguardando a conclusão de IO, então é não usar tempo de CPU, toda a questão da infra-estrutura de controle fio no kernel é que a CPU vai encontrar algo útil para fazer. A única maneira de "mexer os polegares", como sugerido aqui, seria criar um loop de pesquisa, e ninguém que codificou um servidor da Web real é inepto o suficiente para fazer isso.
2) "Threads are hard", só faz sentido no contexto do compartilhamento de dados. Se você tiver threads essencialmente independentes, como é o caso ao lidar com solicitações independentes da Web, o encadeamento é trivialmente simples, basta codificar o fluxo linear de como lidar com um trabalho e ficar tranquilo sabendo que ele tratará vários pedidos e cada um será efetivamente independente. Pessoalmente, eu arriscaria que, para a maioria dos programadores, aprender o mecanismo de fechamento / retorno de chamada é mais complexo do que simplesmente codificar a versão do thread de cima para baixo. (Mas sim, se você precisar se comunicar entre os threads, a vida fica muito difícil muito rapidamente, mas não estou convencido de que o mecanismo de fechamento / retorno de chamada realmente mude isso, apenas restringe suas opções, porque essa abordagem ainda é possível com threads Enfim, isso '
3) Até agora, ninguém apresentou nenhuma evidência real de por que um tipo específico de mudança de contexto consumiria mais ou menos tempo do que qualquer outro tipo. Minha experiência na criação de kernels multitarefa (em pequena escala para controladores incorporados, nada tão sofisticado quanto um sistema operacional "real") sugere que esse não seria o caso.
4) Todas as ilustrações que vi até agora que pretendem mostrar o quão mais rápido o Node é do que outros servidores da Web são terrivelmente falhas, no entanto, são falhas de uma maneira que ilustra indiretamente uma vantagem que eu definitivamente aceitaria para o Node (e não é de forma alguma insignificante). O nó não parece precisar (ou até mesmo permitir) de ajuste. Se você tiver um modelo encadeado, precisará criar encadeamentos suficientes para lidar com a carga esperada. Faça isso mal e você terá um desempenho ruim. Se houver muito poucos encadeamentos, a CPU estará ociosa, mas incapaz de aceitar mais solicitações, criar muitos encadeamentos e você desperdiçará memória do kernel e, no caso de um ambiente Java, também estará desperdiçando a memória heap principal . Agora, para Java, desperdiçar heap é a primeira e melhor maneira de estragar o desempenho do sistema, porque a coleta eficiente de lixo (atualmente, isso pode mudar com o G1, mas parece que o júri ainda está nesse ponto desde o início de 2013, pelo menos) depende de ter muita pilha de reposição. Portanto, existe o problema: ajuste-o com muito poucos threads, você terá CPUs ociosas e taxa de transferência ruim, ajuste-o com muitos threads e atolará de outras maneiras.
5) Existe outra maneira pela qual aceito a lógica da afirmação de que a abordagem do Node "é mais rápida por design", e é isso. A maioria dos modelos de encadeamento usa um modelo de comutação de contexto com fatias de tempo, em camadas sobre o modelo preemptivo mais apropriado (alerta de julgamento do valor :) e mais eficiente (não um julgamento do valor). Isso acontece por duas razões: primeiro, a maioria dos programadores parece não entender a preempção de prioridade; e, segundo, se você aprender a segmentação em um ambiente Windows, o intervalo de tempo existe, quer você goste ou não (é claro, isso reforça o primeiro ponto) ; notavelmente, as primeiras versões do Java usavam preempção de prioridade nas implementações do Solaris e tempo no Windows. Porque a maioria dos programadores não entendeu e reclamou que "o encadeamento não funciona no Solaris" eles mudaram o modelo para timeslice em todos os lugares). De qualquer forma, a linha inferior é que o timelicing cria opções de contexto adicionais (e potencialmente desnecessárias). Cada troca de contexto leva tempo de CPU, e esse tempo é efetivamente removido do trabalho que pode ser feito no trabalho real em questão. No entanto, a quantidade de tempo investido na mudança de contexto por causa da divisão do tempo não deve ser superior a uma porcentagem muito pequena do tempo total, a menos que algo bastante estranho esteja acontecendo, e não há motivo para esperar que esse seja o caso em um servidor web simples). Portanto, sim, as excessivas alternâncias de contexto envolvidas na divisão do tempo são ineficientes (e isso não ocorre em e esse tempo é efetivamente removido do trabalho que pode ser feito no trabalho real em questão. No entanto, a quantidade de tempo investido na mudança de contexto por causa da divisão do tempo não deve ser superior a uma porcentagem muito pequena do tempo total, a menos que algo bastante estranho esteja acontecendo, e não há motivo para esperar que esse seja o caso em um servidor web simples). Portanto, sim, as excessivas alternâncias de contexto envolvidas na divisão do tempo são ineficientes (e isso não ocorre em e esse tempo é efetivamente removido do trabalho que pode ser feito no trabalho real em questão. No entanto, a quantidade de tempo investido na mudança de contexto por causa da divisão do tempo não deve ser superior a uma porcentagem muito pequena do tempo total, a menos que algo bastante estranho esteja acontecendo, e não há motivo para esperar que esse seja o caso em um servidor web simples). Portanto, sim, as excessivas alternâncias de contexto envolvidas na divisão do tempo são ineficientes (e isso não ocorre emcomo regra, os threads do kernel ), mas a diferença será de alguns por cento da taxa de transferência, e não do tipo de fatores de número inteiro que estão implícitos nas declarações de desempenho que geralmente estão implícitas no Node.
De qualquer forma, peço desculpas por tudo isso ser longo e desmedido, mas eu realmente sinto que até agora, a discussão não provou nada, e eu ficaria feliz em ouvir alguém de uma dessas situações:
a) uma explicação real de por que o Node deve ser melhor (além dos dois cenários que descrevi acima, o primeiro dos quais (mau ajuste) acredito ser a explicação real de todos os testes que vi até agora. ], na verdade, quanto mais eu penso sobre isso, mais me pergunto se a memória usada por um grande número de pilhas pode ser significativa aqui. Os tamanhos de pilha padrão para threads modernos tendem a ser bastante grandes, mas a memória alocada por um sistema de eventos baseado em fechamento seria apenas o necessário)
b) uma referência real que realmente oferece uma boa chance para o servidor de escolha. Pelo menos dessa maneira, eu teria que parar de acreditar que as afirmações são essencialmente falsas; referências mostradas não são razoáveis).
Saúde, Toby
select()
é mais rápida que as trocas de contexto de thread.