Threading vs thread único

É sempre garantido que um aplicativo multi-thread seria executado mais rápido do que um único aplicativo encadeado?

Eu tenho dois segmentos que preenche dados de uma fonte de dados, mas entidades diferentes (por exemplo: database, de duas tabelas diferentes), parece que a versão de thread única do aplicativo está sendo executada mais rapidamente do que a versão com dois threads.

Por que a razão seria? Quando eu olho para o monitor de desempenho, ambos os cpu s são muito spikey? isso é devido a mudança de contexto?

Quais são as melhores práticas para aproveitar a CPU e utilizá-lo totalmente?

Espero que isso não seja ambíguo.

Uma analogia pode ajudar.

Você tem um monte de cartas que você precisa entregar em vários endereços da cidade. Então você contrata um cara com uma moto para entregar suas cartas.

Os sinais de trânsito na sua cidade são sinais de trânsito perfeitos . Eles são sempre verdes, a menos que haja alguém no cruzamento.

O cara da moto anda por aí entregando um monte de cartas. Como não há mais ninguém na estrada, toda luz é verde, o que é incrível. Mas você acha que isso pode ser mais rápido. Eu sei, vou contratar outro motorista.

O problema é ** você só tem uma motocicleta ainda *. Então, agora seu primeiro motorista dirige por aí na motocicleta por um tempo, e então de vez em quando pára, sai, e o segundo motorista sobe, pula e dirige por aí.

Isso é mais rápido? Não, claro que não. Isso é mais lento . Adicionando mais threads não faz nada mais rápido. Tópicos não são mágicos . Se um processador é capaz de fazer um bilhão de operações por segundo, a adição de outro thread não subitamente torna disponível outro bilhão de operações por segundo. Pelo contrário, rouba resources de outros tópicos. Se uma motocicleta pode percorrer 100 milhas por hora, parar a moto e fazer com que outro piloto entre em ação não a torna mais rápida! Claramente, em média, as cartas não estão sendo entregues mais rapidamente neste esquema, elas estão sendo entregues em uma ordem diferente.

OK, e se você contratar dois motoristas e duas motos? Agora você tem dois processadores e um thread por processador, então será mais rápido, certo? Não, porque nos esquecemos dos semáforos. Antes, havia apenas uma motocicleta dirigindo em alta velocidade a qualquer momento. Agora existem dois pilotos e duas motos, o que significa que agora às vezes uma das motos terá que esperar porque a outra está no cruzamento. Mais uma vez, adicionar mais threads diminui a velocidade porque você gasta mais tempo lutando contra bloqueios. Quanto mais processadores você adicionar, pior ficará; você acaba com mais e mais tempo gasto esperando em luzes vermelhas e cada vez menos tempo dirigindo mensagens.

Adicionar mais encadeamentos pode causar uma escalabilidade negativa se isso fizer com que os bloqueios sejam contestados. Quanto mais tópicos, mais contenção, mais lentas as coisas vão.

Suponha que você torne os mecanismos mais rápidos – agora você tem mais processadores, mais threads e processadores mais rápidos. Isso sempre torna mais rápido? NÃO. Frequentemente não. Aumentar a velocidade do processador pode fazer com que os programas multithreads sejam mais lentos . Mais uma vez, pense no trânsito.

Suponha que você tenha uma cidade com milhares de motoristas e sessenta e quatro motocicletas, todos os motoristas indo e voltando entre as motos, algumas das motocicletas em cruzamentos bloqueando outras motocicletas. Agora você faz todas as motos correrem mais rápido. Isso ajuda? Bem, na vida real, quando você está dirigindo por aí, você consegue onde você está indo duas vezes mais rápido em um Porsche como em um Honda Civic? Claro que não; a maior parte do tempo na cidade dirigindo você está preso no trânsito .

Se você pode dirigir mais rápido, muitas vezes você acaba esperando no trânsito mais tempo porque você acaba dirigindo para o congestionamento mais rápido. Se todos forem em direção ao congestionamento mais rápido, o congestionamento piorará .

O desempenho multithreaded pode ser profundamente contra-intuitivo. Se você quer um desempenho extremamente alto, eu recomendo não usar uma solução multithreaded, a menos que você tenha um aplicativo que seja “embaraçosamente paralelo” – ou seja, algum aplicativo que seja obviamente receptivo a múltiplos processadores, como calcular conjuntos Mandelbrot ou fazer raytracing ou alguma coisa assim. E, em seguida, não jogue mais threads no problema do que você tem processadores. Mas, para muitas aplicações, iniciar mais threads atrasa você .

Minha opinião

Não, não é garantido que um aplicativo de vários segmentos seja executado mais rápido do que um único aplicativo encadeado. O principal problema é distribuir adequadamente a carga de trabalho para todos os núcleos disponíveis e minimizar o bloqueio e a alternância de contexto.

Eu acho que algumas das coisas piores que as pessoas podem fazer é ir e tentar multithread cada pequena parte de suas tarefas de uso intensivo de CPU. Às vezes, eles acabam criando centenas de threads e cada thread está tentando realizar muitos cálculos intensivos de CPU. A melhor coisa a fazer nessa situação é criar um (ou talvez dois) segmentos por núcleo.

Nos casos em que há uma interface do usuário envolvida, quase sempre é preferível delegar todo o trabalho intensivo da CPU em threads, a fim de manter a interface do usuário responsiva. Este é provavelmente o uso mais popular para threads.

… parece que a versão single thread do aplicativo está sendo executada mais rapidamente que a versão com dois threads.

Você executou alguma análise de desempenho? Se você não o fez, então o que você observou é um tanto irrelevante.

Quais são as melhores práticas para aproveitar a CPU e utilizá-lo totalmente?

Dada a descrição do seu problema, não parece que seus problemas de desempenho estejam ligados à CPU, mas a E / S vinculada … sua comunicação com o database é muito mais lenta que o cache do seu processador e se é um database de rede do que o seu disco rígido. Seu gargalo de desempenho é com seu database, então tudo que você precisa fazer é criar threads suficientes para maximizar a taxa de transferência de sua conexão com o database.


Diretamente da Wikipedia :

Vantagens

Algumas vantagens incluem:

  • Se um encadeamento obtiver muitos erros de cache, os outros encadeamentos podem continuar, aproveitando os resources de computação não utilizados, o que pode levar a uma execução geral mais rápida, já que esses resources estariam inativos se apenas um único encadeamento fosse executado .
  • Se um encadeamento não puder usar todos os resources de computação da CPU (porque as instruções dependem do resultado da outra), a execução de outro encadeamento permite não deixá-las inativas.
  • Se vários segmentos trabalharem no mesmo dataset, eles poderão compartilhar seu cache, levando a um melhor uso do cache ou synchronization em seus valores.

Desvantagens

Algumas críticas de multithreading incluem:

  • Vários encadeamentos podem interferir entre si ao compartilhar resources de hardware, como caches ou TLBs (Translations Lookeside Buffers).
  • Os tempos de execução de um único thread não são melhorados, mas podem ser degradados, mesmo quando apenas um thread está sendo executado. Isso ocorre devido a freqüências mais lentas e / ou estágios adicionais de pipeline necessários para acomodar o hardware de comutação de linha.
  • O suporte de hardware para multithreading é mais visível para o software, exigindo, portanto, mais alterações nos programas aplicativos e nos sistemas operacionais do que o multiprocessing.

Atualizar

Além disso, o servidor de database está na mesma máquina em que o código está sendo executado. não é um servidor sql. é um nosql dbms. então, por favor, não assuma nada sobre o servidor de database.

Alguns sistemas NoSQL são baseados em disco e a leitura do disco de vários encadeamentos é quase garantida para diminuir o desempenho. O disco rígido pode ter que mover a cabeça para diferentes setores do disco ao saltar entre os segmentos e isso é ruim!

Eu entendo o ponto que você queria fazer é a velocidade de IO. mas ainda é a mesma máquina. Por que IO é tão lento?

Seu sistema NoSQL pode ser baseado em disco, portanto, todos os seus dados são armazenados em disco, em vez de carregados na memory (como o SQL Server). Além disso, pense na arquitetura: o disco é um cache para RAM, a RAM é um cache para o cache da CPU e o cache da CPU é para os registradores da CPU. Então Disk -> Ram -> CPU cache -> Registers, existem 3 níveis de cache antes de você chegar aos registradores. Dependendo da quantidade de dados que você está utilizando, você pode estar recebendo muitos erros de cache para ambos os threads em cada um desses níveis … uma falha de cache no cache da CPU carregará mais dados da RAM, uma falha no cache A RAM irá carregar mais dados do disco, tudo isso se traduz em uma taxa de transferência reduzida .

em outros críticos “crie threads suficientes para utilizar …” criar muitos threads também levará tempo. certo?

Na verdade não … você só tem dois tópicos. Quantas vezes você está criando os tópicos? Quantas vezes você está criando? Se você estiver criando apenas dois encadeamentos e estiver fazendo todo o seu trabalho nesses dois encadeamentos por toda a vida útil do aplicativo, praticamente não haverá sobrecarga de desempenho na criação dos encadeamentos com os quais você deve se preocupar.

Se o seu programa é pesado em I / O e passa a maior parte do tempo esperando por I / O (como operação de database), então o threading não rodaria mais rápido.

Se fizer muito cálculo na CPU, ele terá benefício ou não, dependendo de como você o escreve.

Claro que não. O encadeamento impõe sobrecarga, portanto, se os benefícios do aplicativo dependem de como ele é paralelo .

Não não é. Porque quando você faz multi-threading, seu processador tem que alternar entre thread, memory, registro e os custos. Existem algumas tarefas que são divisíveis como merge sort, mas existem algumas tarefas que podem não ser divisíveis para sub-tarefas como checar se um número é primo ou não (é apenas o meu exemplo repentino), e então se você tentar separá-lo fora, ele só funciona como um problema de thread único.

A sobrecarga de comutação de contexto não é um problema até que você tenha centenas de threads. O problema de alternância de contexto é superestimado com frequência (execute o gerenciador de tarefas e notifique quantos encadeamentos já foram iniciados). Os picos observados dependem de comunicações de rede que são bastante instáveis ​​em comparação com os cálculos de cpu locais.

Eu sugiro escrever aplicativos escalonáveis ​​no SEDA (Staged Event Driven Architecture) quando o sistema é composto de vários componentes (5-15) e cada componente tem sua própria fila de mensagens com o pool de threads limitado. Você pode ajustar o tamanho dos pools e até aplicar algoritmos que alteram o tamanho dos pools de threads para tornar alguns componentes mais produtivos do que outros (já que todos os componentes compartilham as mesmas CPUs). Você pode ajustar o tamanho dos pools para um hardware específico que torne os aplicativos SEDA extremamente sintonizáveis.

Eu vi exemplos do mundo real em que o código funcionou tão mal com mais processadores adicionados (contenção de bloqueio horrível entre threads) que o sistema precisava ter processadores removidos para restaurar o desempenho; Então, sim, é possível fazer o código piorar, adicionando mais threads de execução.

Os aplicativos restritos de E / S são outro bom exemplo, mencionado acima.

De acordo com a lei de Amdahl, a velocidade máxima depende da proporção do algoritmo que pode ser paralelizado. Se o algoritmo é altamente paralelo do que aumentar a quantidade de CPUs e threads, haverá um grande aumento. Se o algoritmo não for paralelo (há muito controle de stream de código ou contenção de dados) do que não há ganho ou pode ocorrer a diminuição do desempenho.

insira a descrição da imagem aqui