Estou escrevendo um servidor e envio cada ação para um thread separado quando a solicitação é recebida. Eu faço isso porque quase toda solicitação faz uma consulta ao banco de dados. Estou usando uma biblioteca de threads para reduzir a construção / destruição de threads.
Minha pergunta é: qual é um bom ponto de corte para encadeamentos de E / S como esses? Eu sei que seria apenas uma estimativa aproximada, mas estamos falando de centenas? Milhares?
Como eu iria descobrir qual seria esse ponto de corte?
EDITAR:
Obrigado a todos por suas respostas, parece que vou precisar testá-lo para descobrir meu limite de contagem de threads. A questão é: como sei que atingi esse teto? O que exatamente devo medir?
multithreading
performance
threadpool
ryeguy
fonte
fonte
Respostas:
Algumas pessoas diriam que dois tópicos são muitos - eu não estou nesse campo :-)
Aqui está o meu conselho: meça, não adivinhe. Uma sugestão é torná-lo configurável e, inicialmente, defini-lo como 100, depois libere seu software para o estado selvagem e monitore o que acontece.
Se o uso de seu segmento atingir um pico de 3, 100 será demais. Se permanecer em 100 a maior parte do dia, aumente para 200 e veja o que acontece.
Você pode realmente ter seu próprio código monitorando o uso e ajustando a configuração para a próxima vez que iniciar, mas isso provavelmente é um exagero.
Para esclarecimentos e elaboração:
Eu não estou defendendo a rolagem do seu próprio subsistema de pool de threads, por qualquer meio use o que você possui. Mas, como você estava perguntando sobre um bom ponto de corte para encadeamentos, presumo que a implementação do conjunto de encadeamentos tenha a capacidade de limitar o número máximo de encadeamentos criados (o que é uma coisa boa).
Eu escrevi código de pool de threads e de conexão com o banco de dados e eles têm os seguintes recursos (que eu acredito que são essenciais para o desempenho):
O primeiro define uma linha de base para o desempenho mínimo em termos do cliente do conjunto de encadeamentos (esse número de encadeamentos está sempre disponível para uso). O segundo define uma restrição ao uso de recursos por encadeamentos ativos. O terceiro retorna à linha de base em tempos de silêncio, para minimizar o uso de recursos.
Você precisa equilibrar o uso de recursos de ter threads não utilizados (A) com o uso de recursos de não ter threads suficientes para fazer o trabalho (B).
(A) geralmente é uso de memória (pilhas e assim por diante), uma vez que um encadeamento que não funciona não estará usando muito da CPU. (B) geralmente será um atraso no processamento de solicitações à medida que elas chegarem, conforme for necessário aguardar a disponibilidade de um encadeamento.
É por isso que você mede. Como você afirma, a grande maioria dos seus threads estará aguardando uma resposta do banco de dados para que eles não estejam em execução. Existem dois fatores que afetam quantos threads você deve permitir.
O primeiro é o número de conexões de banco de dados disponíveis. Esse pode ser um limite rígido, a menos que você possa aumentá-lo no DBMS - presumo que seu DBMS possa ter um número ilimitado de conexões nesse caso (embora você deva medir isso também).
Então, o número de threads que você deve ter depende do seu uso histórico. O mínimo que você deve executar é o número mínimo que você já executou + A%, com um mínimo absoluto de (por exemplo, e configurá-lo como A) 5.
O número máximo de threads deve ser o seu máximo histórico + B%.
Você também deve monitorar as mudanças de comportamento. Se, por algum motivo, seu uso atingir 100% da disponibilidade por um tempo significativo (para afetar o desempenho dos clientes), você deverá aumentar o máximo permitido até que seja novamente B% mais alto.
Em resposta ao "o que exatamente devo medir?" questão:
O que você deve medir especificamente é a quantidade máxima de encadeamentos em uso simultâneo (por exemplo, aguardando um retorno da chamada do DB) sob carga. Em seguida, adicione um fator de segurança de 10%, por exemplo (enfatizado, pois outros pôsteres parecem usar meus exemplos como recomendações fixas).
Além disso, isso deve ser feito no ambiente de produção para ajuste. Não há problema em obter uma estimativa antecipadamente, mas você nunca sabe qual produção será o seu caminho (e é por isso que todas essas coisas devem ser configuráveis em tempo de execução). Isso é para capturar uma situação como duplicação inesperada das chamadas de clientes que chegam.
fonte
Esta questão foi discutida minuciosamente e não tive a chance de ler todas as respostas. Mas aqui estão algumas coisas a serem levadas em consideração ao examinar o limite superior do número de encadeamentos simultâneos que podem coexistir pacificamente em um determinado sistema.
Agora você pode ajustar o tamanho da pilha para incorporar mais threads, mas é necessário levar em consideração as despesas gerais do gerenciamento de threads (criação / destruição e agendamento). Você pode impor a afinidade da CPU a um determinado processo e a um determinado encadeamento para vinculá-los a CPUs específicas, a fim de evitar sobrecargas de migração de encadeamento entre as CPUs e evitar problemas de dinheiro a frio.
Observe que é possível criar milhares de threads a seu gosto, mas quando o Linux fica sem VM, ele aleatoriamente começa a matar processos (portanto, threads). Isso evita que o perfil do utilitário seja atingido no máximo. (A função de utilitário informa sobre o utilitário em todo o sistema para uma determinada quantidade de recursos. Com recursos constantes, neste caso, Ciclos e memória da CPU, a curva do utilitário se achata com um número cada vez maior de tarefas).
Estou certo de que o agendador do kernel do Windows também faz algo desse tipo para lidar com a utilização excessiva dos recursos
[1] http://adywicaksono.wordpress.com/2007/07/10/i-can-not-create-more-than-255-threads-on-linux-what-is-the-solutions/
fonte
Se seus threads estiverem realizando algum tipo de trabalho com muitos recursos (CPU / Disco), raramente você verá benefícios além de um ou dois, e muitos prejudicarão o desempenho muito rapidamente.
A melhor opção é que seus threads posteriores parem enquanto os primeiros forem concluídos, ou alguns terão bloqueios de sobrecarga baixa em recursos com baixa contenção. Na pior das hipóteses, você começa a debulhar o cache / disco / rede e sua taxa de transferência geral cai pelo chão.
Uma boa solução é colocar solicitações em um pool que são despachadas para threads de trabalho de um pool de threads (e sim, evitar a criação / destruição contínua de threads é uma excelente primeira etapa).
O número de encadeamentos ativos nesse pool pode ser ajustado e escalonado com base nas descobertas de sua criação de perfil, no hardware em que você está executando e em outras coisas que podem estar ocorrendo na máquina.
fonte
Uma coisa que você deve ter em mente é que o python (pelo menos a versão baseada em C) usa o que é chamado de bloqueio global de intérpretes que pode ter um enorme impacto no desempenho em máquinas com vários núcleos.
Se você realmente precisa do máximo de python multithread, considere usar o Jython ou algo assim.
fonte
Como Pax disse com razão, meça, não adivinhe . Que o que fiz para a testemunha de DNS e os resultados foi surpreendente: o número ideal de threads foi muito maior do que eu pensava, algo como 15.000 threads para obter os resultados mais rápidos.
Claro, isso depende de muitas coisas, é por isso que você deve se medir.
Medidas completas (somente em francês) no Combien de fils d'exécution? .
fonte
Eu escrevi vários aplicativos altamente multiencadeados. Geralmente, permito que o número de threads em potencial seja especificado por um arquivo de configuração. Quando eu ajustei para clientes específicos, defini o número alto o suficiente para que minha utilização de todos os núcleos da CPU fosse bastante alta, mas não tão alta que eu tenha problemas de memória (esses eram sistemas operacionais de 32 bits no Tempo).
Em outras palavras, quando você atinge algum gargalo, seja CPU, taxa de transferência de banco de dados, taxa de transferência de disco, etc., adicionar mais threads não aumentará o desempenho geral. Mas até você atingir esse ponto, adicione mais tópicos!
Observe que isso pressupõe que o (s) sistema (s) em questão sejam dedicados ao seu aplicativo e você não precisa jogar muito bem (evitar morrer de fome) outros aplicativos.
fonte
A resposta "big iron" geralmente é um encadeamento por recurso limitado - processador (limite da CPU), braço (limite de E / S) etc. -, mas isso só funciona se você puder rotear o trabalho para o segmento correto do recurso. ser acessado.
Onde isso não for possível, considere que você tem recursos fungíveis (CPUs) e recursos não fungíveis (armas). Para CPUs, não é essencial atribuir cada thread a uma CPU específica (embora isso ajude no gerenciamento de cache), mas para os braços, se você não pode atribuir um segmento ao braço, entra na teoria das filas e qual é o número ideal para manter os braços ocupado. Geralmente, estou pensando que, se você não puder rotear solicitações com base no braço usado, ter 2-3 segmentos por braço será quase certo.
Uma complicação ocorre quando a unidade de trabalho passada para o encadeamento não executa uma unidade de trabalho razoavelmente atômica. Por exemplo, você pode ter o encadeamento em um ponto acessando o disco, em outro momento aguardando na rede. Isso aumenta o número de "rachaduras" nas quais threads adicionais podem entrar e realizar um trabalho útil, mas também aumenta a oportunidade de threads adicionais poluirem os caches uns dos outros, etc., e atolar o sistema.
Obviamente, você deve pesar tudo isso contra o "peso" de um fio. Infelizmente, a maioria dos sistemas possui threads muito pesados (e o que eles chamam de "threads leves" geralmente não são threads), por isso é melhor errar no lado inferior.
O que eu vi na prática é que diferenças muito sutis podem fazer uma enorme diferença na quantidade de threads ideal. Em particular, problemas de cache e conflitos de bloqueio podem limitar bastante a quantidade de simultaneidade prática.
fonte
Uma coisa a considerar é quantos núcleos existem na máquina que executará o código. Isso representa um limite rígido para quantos threads podem continuar a qualquer momento. No entanto, se, como no seu caso, espera-se que os threads aguardem com freqüência por um banco de dados para executar uma consulta, você provavelmente desejará ajustá-los com base em quantas consultas simultâneas o banco de dados pode processar.
fonte
Acho que isso é um pouco esquivo para sua pergunta, mas por que não colocá-los em processos? Meu entendimento sobre redes (desde os dias nebulosos de antigamente, eu realmente não codifico redes) é que cada conexão de entrada pode ser tratada como um processo separado, porque, se alguém faz algo desagradável em seu processo, isso não acontece. nuke o programa inteiro.
fonte
ryeguy, atualmente estou desenvolvendo um aplicativo semelhante e meu número de threads é definido como 15. Infelizmente, se eu aumentar para 20, ele trava. Então, sim, acho que a melhor maneira de lidar com isso é medir se sua configuração atual permite ou não mais ou menos que um número X de threads.
fonte
Na maioria dos casos, você deve permitir que o pool de threads lide com isso. Se você publicar algum código ou fornecer mais detalhes, poderá ser mais fácil verificar se há algum motivo para o comportamento padrão do pool de threads não ser o melhor.
Você pode encontrar mais informações sobre como isso deve funcionar aqui: http://en.wikipedia.org/wiki/Thread_pool_pattern
fonte
Tantos threads quanto os núcleos da CPU é o que ouvi muitas vezes.
fonte