Programas que afirmam não serem compatíveis com vários núcleos

17

Você vê essa frase ou algo semelhante de vez em quando, geralmente referindo-se a um programa que afirma que não foi projetado para aproveitar ao máximo os processadores com vários núcleos. Isso é comum, especialmente com a programação de videogames. (é claro que muitos programas não têm simultaneidade e não precisam dele, como scripts básicos, etc.).

Como isso pode ser? Muitos programas (especialmente jogos) usam inerentemente a simultaneidade e, como o sistema operacional é responsável pelo agendamento de tarefas na CPU, esses programas não estão tirando proveito inerente dos vários núcleos disponíveis? O que significaria neste contexto "tirar proveito de múltiplos núcleos"? Esses desenvolvedores estão realmente proibindo o agendamento de tarefas do SO e forçando a afinidade ou o próprio agendamento? (Parece um grande problema de estabilidade).

Eu sou um programador Java, então talvez eu não tenha tido que lidar com isso devido a abstrações ou outros enfeites.

SnakeDoc
fonte
11
Uma grande possibilidade é que atalhos foram executados na sincronização, que funcionam para um sistema de processador único / núcleo, mas rompem com a simultaneidade real de vários processadores / núcleos.
Bart van Ingen Schenau
@BartvanIngenSchenau: Isso está correto. Você deve expandir isso e publicá-lo como resposta. Eu acho que todos os outros erraram o ponto.
kevin Cline
1
Eu acho que o @Bart está muito perto. No entanto, s / work / parece funcionar / e estará mais próximo da marca.
Ben Voigt
como um aparte - eu tive essa experiência como usuário e não como programador - Ground Control 2 no Windows XP. Eu precisava definir a afinidade do núcleo para apenas um núcleo em um sistema multicore para que ele funcionasse corretamente; caso contrário, todas as animações (na verdade todo o jogo) rodariam em velocidade 10x, o que, apesar de ser um desafio maior, ficou um pouco irritante depois de um tempo. . Eu não fiz nenhum trabalho em jogos, mas, em minha opinião, algumas partes do jogo pareciam depender do processador apenas realizando uma certa quantidade de trabalho ao mesmo tempo.
jammypeach

Respostas:

28

Uma boa concorrência requer muito mais do que lançar alguns threads em um aplicativo e esperar o melhor. Há uma variedade de como um programa simultâneo pode passar de embaraçosamente paralelo a puro seqüencial. Qualquer programa pode usar a lei de Amdahl para expressar quão escalável é um problema ou algoritmo. Algumas qualificações para uma aplicação embaraçosamente paralela seriam:

  • Sem estado compartilhado, todas as funções dependem apenas dos parâmetros passados ​​no
  • Sem acesso a dispositivos físicos (placas gráficas, discos rígidos etc.)

Existem outras qualificações, mas com apenas essas duas podemos entender por que os jogos em particular não são tão fáceis quanto você imagina para tirar proveito de múltiplos núcleos. Por um lado, o modelo do mundo que será renderizado deve ser compartilhado, pois diferentes funções calculam física, movimento, aplicam inteligência artificial etc. Segundo, cada quadro deste modelo de jogo deve ser renderizado na tela com uma placa gráfica.

Para ser justo, muitos criadores de jogos usam mecanismos de jogos produzidos por terceiros. Demorou um pouco, mas esses mecanismos de jogos de terceiros agora são muito mais paralelos do que costumavam ser.

Existem desafios arquitetônicos maiores ao lidar com simultaneidade eficaz

A simultaneidade pode assumir várias formas, desde a execução de tarefas em segundo plano até um suporte arquitetural completo para simultaneidade. Alguns idiomas oferecem recursos de simultaneidade muito poderosos, como o ERLANG , mas exige que você pense de maneira muito diferente sobre como você constrói seu aplicativo.

Nem todo programa realmente precisa da complexidade do suporte multicore completo. Um exemplo é o software tributário ou qualquer aplicativo orientado a formulários. Quando a maior parte do tempo é gasta esperando o usuário fazer alguma coisa, a complexidade dos aplicativos multithread não é tão útil.

Alguns aplicativos se prestam a uma solução paralela mais embaraçosa, como aplicativos da web. Nesse caso, a plataforma começa embaraçosamente paralela e você não precisa impor a contenção de threads.

A linha inferior:

Nem todos os aplicativos são realmente prejudicados por não aproveitarem vários threads (e, portanto, núcleos). Para aqueles que são prejudicados por isso, às vezes os cálculos não são amigáveis ​​ao processamento paralelo ou a sobrecarga para coordenar isso tornaria o aplicativo mais frágil. Infelizmente, o processamento paralelo ainda não é tão fácil quanto deveria ser.

Berin Loritsch
fonte
Esta é uma ótima análise. Uma coisa que me incomoda, porém, é o seu argumento sobre os programas do mundo real, muitas vezes não serem embaraçosamente paralelos e, portanto, difíceis de paralelizar: embora possa ser impossível fazer a mesma coisa em paralelo, pode ser muito fácil fazer coisas diferentes em paralelo ( por exemplo, em uma arquitetura de pipeline ou com um thread de interface do usuário separado).
amon
8
O ponto real é que você precisa projetar para execução paralela e, caso contrário, fica restrito por sua falta de design. Concordo que pode ser muito fácil fazer coisas diferentes em paralelo, mas não se for um aplicativo existente com grandes expectativas do usuário. Nesse caso, pode ser necessário reescrever muito bem para tornar isso possível. As reescritas são inerentemente arriscadas, mas, ocasionalmente, você pode apresentar um bom argumento para elas. Fiz algumas reescritas que maximizavam o processamento paralelo, preservando o máximo de código possível. Há muitos fatores ocultos.
Berin Loritsch 27/02
Ótima resposta. Vale a pena enfatizar que não apenas pode haver retornos decrescentes ao paralelizar alguns sistemas, mas alguns podem de fato se tornar mais lentos devido à sobrecarga necessária para torná-los paralelos. Em particular, muitos semáforos / bloqueios e alternância de contexto podem ter efeitos adversos no tempo de execução. A troca de contexto, em particular, pode reduzir a eficácia do cache, o que é uma preocupação não trivial se você estiver no ponto de otimizar seu sistema. O exemplo de mecanismos de jogos da OP em particular me leva a lembrar de ouvir muito mais sobre a otimização do cache do que o acesso paralelo.
Gankro
35

Muitos programas (especialmente jogos) usam inerentemente simultaneidade,

Não, na verdade é o contrário. A maioria dos aplicativos é gravada em uma única mentalidade encadeada e o (s) desenvolvedor (es) nunca fez as alterações necessárias para oferecer suporte à simultaneidade.

Em C, C ++ e C #, você precisa informar explicitamente ao aplicativo para iniciar novos threads e / ou processos.

Eu acho que você está se concentrando demais no agendamento dos threads e não o suficiente na manipulação de dados nos possíveis threads. Compartilhar dados entre threads e / ou processos requer alguma forma de sincronização. Se você alterar um aplicativo para usar vários encadeamentos, mas falhar na sincronização, provavelmente verá muito difícil rastrear bugs no código.

Para os aplicativos multithread em que trabalhei, geralmente nunca me preocupei com o envio e apenas com a sincronização de dados. As únicas vezes em que tive que me preocupar com a expedição foi quando estava perseguindo as condições de corrida devido à sincronização incorreta dos dados.

Geralmente, quando um aplicativo diz que não pode usar vários núcleos, significa que eles não têm a sincronização para proteger a manipulação de dados.


fonte
Isso é verdade mesmo para novos programas modernos de grandes desenvolvedores / editores? Quando me sento e escrevo um programa, uma das primeiras coisas no estágio de design em que penso é: preciso de simultaneidade? Porque isso pode resultar em um design drasticamente diferente. Os jogos em particular devem ter algum nível de simultaneidade; caso contrário, o jogo congelaria quando um dos milhares de modelos na tela tentasse fazer algo ...?
SnakeDoc 27/02
5
@SnakeDoc - Acho que você está confundindo seus domínios por lá. As empresas de Big Game certamente escrevem com a simultaneidade em mente, mas ainda não vi um jogo de uma empresa de Big Game que não suporte a simultaneidade. Os aplicativos e jogos que eu vi que não suportam simultaneidade geralmente são de lojas menores / desenvolvedores individuais, onde eles não teriam começado com essa mentalidade. E, em algum momento da evolução do aplicativo, torna-se impossível concordar após o fato. E alguns aplicativos nunca foram feitos para justificar a simultaneidade.
E também alguns jogos prosperam com novos conteúdos (gráficos e jogabilidade), sem a necessidade de atualizar o mecanismo do jogo (implementação de código). Assim, o mecanismo do jogo pode estar anos atrás em tecnologia.
Rwong
6
@SnakeDoc: Você não precisa de simultaneidade para lidar com milhares de modelos na tela. Não é como se todo objeto em seu jogo precisasse de seu próprio segmento para simulá-lo; um thread pode lidar com as atualizações de tudo na tela a cada passo do tempo.
User2357112 suporta Monica
13

Não se trata tanto de múltiplos núcleos, mas de vários threads. O sistema operacional pode agendar um encadeamento para execução em qualquer núcleo que desejar, e esse agendamento é transparente para o programa que está sendo agendado. No entanto, muitos programas não são gravados usando vários encadeamentos, portanto, eles podem ser executados apenas em um núcleo por vez.

Por que eu escreveria um programa de thread único? Eles são mais fáceis de escrever e mais fáceis de depurar: uma coisa acontece após a outra (em vez de várias coisas acontecerem ao mesmo tempo e possível se interpor). Ou seu programa pode não ter como alvo máquinas com vários núcleos (como foi o caso de jogos antigos). Em alguns casos, um programa multithread pode até rodar mais lentamente que uma versão single threaded se a sobrecarga das alternâncias de contexto e a comunicação entre threads exceder a velocidade obtida pela execução paralela (algumas partes do programa podem não ser paralelizáveis).

amon
fonte
8

Esta não é uma resposta completa. É um conto de advertência.

Um dia, pensei em mostrar aos alunos do meu curso de programação simultâneo uma classificação rápida paralela. O mercúrio deve ser bem paralelo, pensei. Eu usei dois threads. Executei no meu computador single core. Os resultados foram:

  • 14 segundos para uma versão de thread único.
  • 15 segundos para a versão de 2 threads.

Era sobre o que eu esperava.

Então eu tentei em uma máquina mais nova de núcleo duplo.

  • 11 segundos para a versão single-threaded.
  • 20 segundos para a versão de 2 threads.

Os dois threads compartilharam uma fila de tarefas restantes. Parece que os campos do objeto de fila estavam sendo embaralhados entre o cache de um núcleo e o outro.

Theodore Norvell
fonte
2
Com quantos elementos da matriz você testou? Talvez o mergesort seja mais adequado, pois a programação multinúcleo exigiria a cópia de dados para evitar conflitos de linha de cache?
rwong 28/02
2
@rwong Havia 10.000.000 elementos de matriz. Certamente a fusão seria paralela. Se eu tivesse usado a classificação por mesclagem, provavelmente não teria aprendido uma lição útil.
Theodore Norvell 28/02
1
@ArlaudPierre Vou considerar paralelizar qualquer algoritmo. O Quicksort é interessante, pois você pode usar a abordagem de saco de tarefas. Como as tarefas são independentes, minha intuição era que deveria ser um exemplo de paralelismo embaraçoso. Devo mencionar que, depois de um pouco de ajuste, ele realmente tem uma aceleração de cerca de 2.
Theodore Norvell
1
@Jules A resposta é o balanceamento de carga. Também queria escrever de uma maneira que facilitasse a alteração do número de threads. Sua abordagem generaliza bem para potências de 2, mas não tão bem para outros números de threads.
Theodore Norvell 28/02
2
@MaciejPiechotka Os costumes são praticamente todas as coisas que você sugere. Mas voltando ao OP, acho que a moral mais relevante é que um programa multithread pode realmente rodar (muito) mais devagar em uma arquitetura com vários núcleos do que em um único processador, a menos que tenha sido feito um esforço para garantir o contrário.
Theodore Norvell