Pergunta

Estou criando o perfil de um programa multithread em execução com diferentes números de threads permitidos. Aqui estão os resultados de desempenho de três execuções do mesmo trabalho de entrada.

1 thread:
  Total thread time: 60 minutes.
  Total wall clock time: 60 minutes.

10 threads:
  Total thread time: 80 minutes. (Worked 33% longer)
  Total wall clock time: 18 minutes.  3.3 times speed up

20 threads
  Total thread time: 120 minutes. (Worked 100% longer)
  Total wall clock time: 12 minutes.  5 times speed up

Uma vez que leva mais tempo para fazer o mesmo trabalho, acho que os tópicos devem estar disputando recursos.

Já examinei os quatro pilares (cpu, memória, diskIO, rede) na máquina do aplicativo e no servidor de banco de dados. A memória era o recurso original em disputa, mas isso foi corrigido agora (mais de 1G livre o tempo todo). A CPU oscila entre 30% e 70% no teste de 20 threads, então há muito. diskIO é praticamente nenhum na máquina do aplicativo e mínimo no servidor de banco de dados. A rede é realmente ótima.

Eu também criei o perfil de código com redgate e não vejo métodos esperando por bloqueios. Ajuda o fato de os threads não estarem compartilhando instâncias. Agora estou verificando itens com mais nuances, como estabelecimento / pool de conexão de banco de dados (se 20 threads tentarem se conectar ao mesmo banco de dados, eles terão que esperar um pelo outro?).

Estou tentando identificar e resolver a contenção de recursos, de forma que a execução de 20 threads fique assim:

20 threads
  Total thread time: 60 minutes. (Worked 0% longer)
  Total wall clock time: 6 minutes.  10 times speed up

Quais são as fontes mais prováveis ​​(além das 4 grandes) que devo procurar para encontrar essa controvérsia?


O código que cada thread executa é aproximadamente:

Run ~50 compiled LinqToSql queries
Run ILOG Rules
Call WCF Service which runs ~50 compiled LinqToSql queries, returns some data
Run more ILOG Rules
Call another WCF service which uses devexpress to render a pdf, returns as binary data
Store pdf to network
Use LinqToSql to update/insert. DTC is involved: multiple databases, one server.

Os serviços WCF estão sendo executados na mesma máquina, não têm estado e podem lidar com várias solicitações simultâneas.


A máquina tem 8 cpu's.

Foi útil?

Solução

O que você descreve é ​​que deseja uma escalabilidade de 100%, que é uma relação de 1: 1 entre o aumento no thread se a diminuição no tempo de bloqueio ... este é normalmente um objetivo, mas difícil de alcançar ...

Por exemplo, você escreve que não há contenção de memória porque há 1 GB livre ... isso é IMHO uma suposição errada ... contenção de memória significa também que se dois threads tentarem alocar memória, pode acontecer que um tenha que aguarde o outro ... outro ponto a ter em mente são as interrupções que acontecem pelo GC que congela todos os threads temporariamente ... o GC pode ser customizado um pouco via configuração (gcServer) - veja http://blogs.msdn.com/b/clyon/archive/2004/09/08 /226981.aspx

Outro ponto é o serviço WCF chamado ... se ele não puder ser ampliado - por exemplo, a renderização de PDF - então isso também é uma forma de contenção, por exemplo ...

A lista de possíveis contendas é "interminável" ... e nem sempre nas áreas óbvias que você mencionou ...

EDITAR - conforme comentários:

Alguns pontos a verificar:

  • pool de conexão
    qual provedor você usa? como está configurado?
  • Renderização de PDF
    possível contenção seria medida em algum lugar dentro da biblioteca que você usa ...
  • Linq2SQL
    Verifique os planos de execução para todas essas consultas ... pode ser que algumas obtenham qualquer tipo de bloqueio e, portanto, possivelmente criem uma contenção do lado do servidor de banco de dados ...

EDITAR 2:

Tópicos
Esses tópicos são do ThreadPool? Se sim, então você não escalará :-(

EDITAR 3:

Threads ThreadPool são ruins para tarefas de longa duração, o que é o caso em seu cenário ... para obter detalhes, consulte

De http://www.yoda.arachsys.com/csharp/ threads / printable.shtml

Operações de longa duração devem usar threads recém-criados; operações de execução curta podem tirar proveito do pool de threads.

Se você deseja desempenho extremo, vale a pena conferir CQRS e o real exemplo mundial descrito como LMAX .

Outras dicas

Em vez de medir o tempo total de thread, meça o tempo para cada uma das operações que você faz que fazem I / O de algum tipo (banco de dados, disco, rede, etc.).

Suspeito que você descobrirá que essas operações são as que demoram mais quando você tem mais threads, e isso ocorre porque a contenção está na outra extremidade dessa E / S.Por exemplo, seu banco de dados pode serializar solicitações para consistência de dados.

sim, há contenção de recursos. Todos os threads têm que ler / escrever dados no mesmo barramento de memória, direcionados aos mesmos módulos de RAM, por exemplo. Não importa a quantidade de RAM livre, é importante que as leituras / gravações sejam realizadas pelo mesmo controlador de memória nos mesmos módulos de RAM e que os dados sejam transportados pelo mesmo barramento.

Se houver qualquer tipo de sincronização em qualquer lugar , então esse também é um recurso contendido. Se houver alguma E / S, é um recurso contido.

Você nunca verá um aumento de velocidade N x ao passar de 1 para N tópicos. Não é possível porque, em última análise, tudo na CPU é um recurso compartilhado no qual haverá algum grau de contenção.

Existem vários fatores que o impedem de obter a aceleração linear total. Você está assumindo que o banco de dados, o servidor em que o banco de dados está sendo executado, a rede que o conecta ao cliente, o computador cliente, o sistema operacional e os drivers em ambas as extremidades, o subsistema de memória, E / S de disco e tudo < / em> no meio é capaz de ir 20 vezes mais rápido quando você passa de 1 a 20 threads.

Duas palavras: continue sonhando.

Cada um desses gargalos precisa apenas diminuir sua velocidade em alguns por cento, então o resultado geral será algo parecido com o que você está vendo.

Tenho certeza de que você pode ajustá-lo para escalar um pouco melhor, mas não espere milagres.

Mas uma coisa que você pode procurar é o compartilhamento de linha de cache. Os threads acessam dados muito próximos dos dados usados ​​por outros threads? Com que frequência você pode evitar que isso ocorra?

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top