Quando um mecanismo de tarefas paralelas se torna uma boa solução?

8

Muitas vezes sou tentado a quebrar o jogo em que estou trabalhando para tentar uma arquitetura baseada em tarefas paralelas, mas isso não parece ser um grande requisito para o meu projeto, por isso evito isso por enquanto. Estou planejando experimentá-lo primeiro em um projeto de brinquedo, para "brincar com o conceito".

Agora, o que estou perguntando é: como muitos jogos (que não são AAA) não exigem um desempenho realmente muito alto, parece que usar um mecanismo baseado em tarefas não vale a pena incomodar até ... quais casos?

No momento, acho que isso é necessário quando você realmente precisa explorar o desempenho máximo do hardware (multi-core). Há outros casos em que é uma boa ideia usar o mecanismo baseado em tarefas? Ou talvez, para novos projetos, seja sempre bom começar com um mecanismo baseado em tarefas?

Klaim
fonte

Respostas:

7

A segmentação é usada em duas situações:

  1. Quando você tem muito trabalho a fazer, mas precisa que a capacidade de resposta do usuário seja mantida.
  2. Quando você precisar do poder de mais de um núcleo de processamento em sua plataforma favorita / requisito mínimo.

Se você puder razoavelmente esperar que uma ou ambas as coisas sejam necessárias, faça isso. Senão, não. Obviamente, ninguém impedirá que você faça uma segmentação por diversão, porque você pode, ou bisbilhotar e investigar, mas em termos de encadeamento para os benefícios de realmente obter um aplicativo multiencadeado, os dois casos de uso acima são praticamente isso.

DeadMG
fonte
2
+1 em resposta: um dos requisitos para o meu projeto principal é não ter tela de carregamento, experiência contínua para imersão máxima. Este é um bom ponto.
Klaim
11
+1 exatamente por que usei vários threads em um jogo de quebra-cabeça: trabalho pesado (verificando milhões de combinações) sem congelar a interface do usuário.
precisa saber é o seguinte
"sem tela de carregamento, experiência contínua" - você terá que construir toda a sua arquitetura em torno dessa idéia, é uma quebra do design de "carregar um nível e jogar nele, depois carregar outro nível" que a maioria dos pequenos motores usa.
Patrick Hughes
14

Se o seu jogo for intensivo em hardware remotamente, você precisará de threads para lidar com todo o hardware moderno; as futuras CPUs lançadas no próximo ano ou dois estão começando a tornar o mínimo de 4 núcleos e até 16 núcleos comuns para os mercados de entusiastas / desempenho. Se você estiver fazendo algum multiencadeamento, faça definitivamente uma arquitetura orientada a tarefas, pois qualquer outro modelo de encadeamento é inerentemente quebrado para mecanismos de jogos prospectivos.

Agora, lembre-se de que com "tarefas" quero dizer "trabalhos" e não "segmentos separados para diferentes subsistemas de mecanismo". Você absolutamente não quer fazer algo como ter um tópico gráfico, um tópico físico, um tópico AI, etc. Isso não ultrapassa um punhado de núcleos e, na verdade, não gera nenhum paralelismo real. A física não deve executar mais de uma atualização por atualização de IA (você deseja que sua AI possa reagir a eventos de física), e os gráficos não têm quase nada de novo para renderizar se a física não for executada, portanto, cada subsistema é executado naturalmente em uma sequência ordem. Você não

O que você quer é fazer é isso. Crie um carretel de linha. Execute o loop do jogo com a sequência clássica de atualizações do subsistema. No entanto, para cada subsistema, separe a carga de trabalho em lotes separáveis ​​distintos e distribua-os ao conjunto de encadeamentos. Aguarde a conclusão de todos os trabalhos antes de executar o próximo estado do loop de atualização do jogo. Alguns subsistemas podem ter várias subestações; por exemplo, os gráficos podem emitir uma série de trabalhos para seleção e, em seguida, uma segunda série de trabalhos para criar a criação de fila. Essa abordagem evita o problema de sincronização da primeira, aumenta para um número muito maior de núcleos e, francamente, é apenas mais fácil de codificar, depurar e manter.

Sean Middleditch
fonte
Obrigado pelas orientações sobre como criar esse tipo de mecanismo, mas já estou bem documentado, portanto não era necessário. É mais a pergunta "quando vale a pena" que eu pergunto. Eu acho que ainda é uma coisa boa para apontar essas informações para aqueles whot não sabem por isso não removê-los :)
Klaim
Parece bastante razoável
Den
Assim como a Apple, depois de se acostumar com os trabalhos, não há como voltar atrás =) Essa é uma boa sugestão de arquitetura para qualquer novo mecanismo.
Patrick Hughes
@SeanMiddleditch Você não está apenas mudando a granularidade do encadeamento dessa maneira? Em vez de apenas um encadeamento por sistema (que na verdade não é tão escalável, alguns sistemas consomem muito mais energia do que outros e também pode haver muito mais sistemas que os núcleos disponíveis), você está executando vários encadeamentos potencialmente por sistema. Portanto, você está ajustando a granularidade dos dados em que cada thread atua. Não tenho certeza de como isso vai acontecer, mas também não tenho nada melhor a acrescentar. Você ainda mantém essa opinião após 7 anos? Obrigado.
Nikos
11
@ Nik-Lz: não, porque "vários threads por sistema" implica que você sabe quais devem ser os threads por sistema. Você deve ter 2 threads para renderização e 3 para física? Quantos para AI? Tudo isso não dependeria da cena específica e até para onde na cena o jogador está olhando e o que está acontecendo no momento? Um sistema de tarefas facilmente escala dinamicamente para as necessidades imediatas, em vez de agrupar um encadeamento inteiro em um subsistema que pode não ser necessário no momento.
Sean Middleditch
0

existem dois usos no multithreading, um é melhorar a performance do programa e o outro é permitir que o programa seja executado quando houver um grande processo em andamento. por exemplo, quando você está tentando carregar alguns dados, é incomodador se o seu programa desligar, então você pode usar outro thread para carregar e manter o Thread principal livre para continuar o loop principal. melhorar o desempenho usando threads é, por outro lado, algo realmente difícil. já que geralmente você faz tudo em um processo linear e isso contrasta com o processamento paralelo. e você sempre deve usar o thread principal para atualizações gráficas de dispositivos, o que dificulta ainda mais a divisão de tarefas entre os threads.

Ali1S232
fonte
11
Rosquear não é difícil. :) É algo que muitas pessoas nunca aprendem a fazer corretamente, então pensam que é difícil devido à inexperiência. Especialmente em jogos, muitos dos algoritmos e estruturas de dados principais são bastante fáceis de lidar com vários threads. As atualizações de física, por exemplo, podem ser divididas em ilhas de objetos e cada uma gerada em um segmento diferente para integração e resolução. Da mesma forma, a seleção de objetos para gráficos é praticamente completamente livre de contenção de acesso a dados de leitura e gravação.
Sean Middleditch
@seanmiddleditch Eu não disse que usar threads em si é uma coisa difícil, mas conseguir usar threads de maneira eficaz e equilibrá-los para compartilhar a mesma carga é algo difícil.
Ali1S232