É um problema de desempenho criar threads que fazem muito loop para verificar as coisas?

8

Essa é uma pergunta genérica que eu sempre quis saber.

Em geral, é intensivo para uma CPU criar threads que executam loops "embora não sejam verdadeiros ..." ou similares?

Por exemplo, suponha que eu faça:

// pseudo-code
new Thread(function() {
    while (!useHasDoneSomething) {
        // do nothing...
    }
    alert("you did something!");
}

Portanto, é apenas um pedaço de código executado em um encadeamento que espera que algo aconteça.

Por alguma razão, imagino que isso pressionaria a CPU. Afinal, mesmo que seja apenas um pequeno loop, ele é executado novamente no instante em que um ciclo é concluído. Isso não significa que ele está passando pelo ciclo muitas vezes por milissegundo? ... Por nanossegundo !?

O sistema operacional "manipula" o programa e os threads, certo? Mas isso ainda dá muita atenção à execução de um loop. Se a CPU não tiver mais nada a fazer, ela não se concentrará apenas nesse loop, consumindo , assim, todo o tempo ocioso ?

E se eu criar 1000 threads que todos façam o mesmo loop? Como isso pode afetar os ciclos de desempenho / CPU?

Além disso, é assim que os eventos funcionam internamente? Quando você está 'ouvindo' um evento em Java, .NET, Javascript etc. (como pressionar um botão ou redimensionar uma janela), foi criado um encadeamento em loop?

Rowan Freeman
fonte
1
O que o criador de perfil diz a você?
Arseni Mourzenko
2
Minha pergunta é genérica. Não é só uma questão de números, mas mais "O que há de errado com meu entendimento?"
Rowan Freeman
Entendi. +1 então.
Arseni Mourzenko

Respostas:

12

Em geral, é intensivo para uma CPU criar threads que executam loops "embora não sejam verdadeiros ..." ou similares?

Sim. O uso desse loop é chamado de espera ocupada e é chamado por um motivo. Esses loops ocupados verificam a condição com a maior frequência e rapidez que o processador pode gerenciá-la, com o resultado de que, se você permanecer no loop por tempo suficiente, a carga do processador será confiável para 100%.

E se eu criar 1000 threads que todos façam o mesmo loop? Como isso pode afetar os ciclos de desempenho / CPU?

Ele só cria mais trabalho para a CPU fazer anotações.

Além disso, é assim que os eventos funcionam internamente? Quando você está 'ouvindo' um evento em Java, .NET, Javascript etc. (como pressionar um botão ou redimensionar uma janela), foi criado um encadeamento em loop?

Não. Os loops ocupados são uma forma de pesquisa e também muito ineficiente. Os sistemas operacionais possuem outros mecanismos para detectar a ocorrência de um evento (por exemplo, uma interrupção ou uma chamada específica) e possuem mecanismos para permitir que um encadeamento o aguarde sem consumir tempo da CPU. Esses mecanismos também serão usados ​​para implementar os mecanismos de eventos de linguagens como Java.

Bart van Ingen Schenau
fonte
Obrigado! E se você dormir o fio por períodos curtos, talvez 100 milissegundos, durante o loop? Isso diminuiria o encadeamento o suficiente para que os ciclos da CPU não fossem desperdiçados com tanta frequência e, portanto, o desempenho se recuperasse?
Rowan Freeman
1
@RowanFreeman: Diminui a carga no processador, mas ainda será maior do que simplesmente esperar o sistema operacional dizer que X aconteceu.
Bart van Ingen Schenau
9

Seu instinto está correto. A espera ocupada assim é descrita como

A rotação pode ser uma estratégia válida em determinadas circunstâncias, principalmente na implementação de spinlocks em sistemas operacionais projetados para execução em sistemas SMP. Em geral, no entanto, a rotação é considerada um antipadrão e deve ser evitada, pois o tempo do processador que poderia ser usado para executar uma tarefa diferente é desperdiçado em atividades inúteis.

Em vez disso, considere uma primitiva de controle de simultaneidade que permita que o encadeamento fique realmente ocioso até que ele tenha um trabalho útil a ser feito, por exemplo, um semáforo ou uma variável de condição :

Para muitas aplicações, a exclusão mútua não é suficiente. Os encadeamentos que tentam uma operação podem precisar esperar até que alguma condição seja verdadeira. Um loop de espera ocupado, como

while not( condition ) do 
   // nothing
end

não funcionará, pois a exclusão mútua impedirá que qualquer outro encadeamento entre no monitor para tornar a condição verdadeira. Existem outras "soluções". Como ter um loop que desbloqueia o monitor, aguarda uma certa quantia, bloqueia o monitor e verifica a condição P. Teoricamente, ele funciona e não entra em conflito, mas surgem problemas. É difícil decidir uma quantidade apropriada de tempo de espera, muito pequena e o encadeamento sobrecarregar a CPU, muito grande e aparentemente não responderá. O que é necessário é uma maneira de sinalizar o encadeamento quando a condição P for verdadeira (ou pode ser verdadeira). A solução são variáveis ​​de condição. Conceitualmente, uma variável de condição é uma fila de encadeamentos, associada a um monitor, na qual um encadeamento pode esperar que alguma condição se torne verdadeira. Assim, cada variável de condição está associada a uma asserção. Enquanto um encadeamento aguarda uma variável de condição, esse encadeamento não é considerado para ocupar o monitor e, portanto, outros encadeamentos podem entrar no monitor para alterar o estado do monitor. Na maioria dos tipos de monitores, esses outros encadeamentos podem sinalizar a variável de condição para indicar que a asserção é verdadeira no estado atual.

Steven Schlansker
fonte
Então, está ocupado esperando muito sucesso? Um único loop (embora disparando constantemente) tem algum problema?
Rowan Freeman
1
@RowanFreeman It is. A espera ocupada, como o texto sugere, continua queimando os ciclos da CPU com instruções inúteis ( ou seja , repetindo if (!useHasDoneSomething)várias vezes, o mais rápido possível). Mesmo que você não se importe com isso em seu programa, perde tempo que outros programas poderiam usar para executar. Mas mesmo dentro do seu programa, um garçom ocupado perde tempo que pode ser atribuído a algum encadeamento de trabalho (caso não haja núcleos suficientes disponíveis para que todos os encadeamentos sejam executados simultaneamente).
afsantos
2
@afsantos Existem usos válidos para a espera ocupada - as alternativas normalmente implicam em desmarcar seu segmento e reagendá-lo mais tarde. Se você sabe que não fará um loop por mais do que alguns ciclos de CPU, pode ser muito mais eficiente aguardar ocupado do que permitir uma alternância de contexto.
assylias
1
@assylias De fato, uma mudança de contexto pode ser mais prejudicial ao desempenho, se soubermos que o tempo de espera é muito curto. No entanto, pela minha experiência, esperas ocupadas são inadequadas com mais frequência do que não. O OP teria que avaliar seu caso individual, para ver se vale a pena ou não.
afsantos 22/05
2

Os encadeamentos devem aguardar eventos de entrada, não alterações de variáveis.

Eventos de entrada são semáforos disponíveis, filas de mensagens não estão vazias ou tempo decorrido:

semGet()
msqRead()
sleep()

Do ponto de vista do encadeamento, essas chamadas de função estão bloqueando: o encadeamento aguarda até o evento chegar, deixando a carga da CPU em outros encadeamentos.

Do ponto de vista do sistema, o encadeamento está marcado como 'Aguardando evento' e não será agendado enquanto o evento não ocorrer.

Sobre os eventos de hardware, eles são tratados através de interrupções, que são sinais elétricos para a CPU e que acionam a execução do ISR (rotinas de serviço de interrupção), cuja função é produzir um evento de software em semáforos, filas de mensagens ou tempo.

mouviciel
fonte
0

A //do nothing parte dentro da condição no cenário normal é WAIT em algum semáforo, ou em outros termos SLEEP. Durma até que alguma interrupção o acorde. E, portanto, o encadeamento entra em "estado de suspensão" ou digamos "estado de não execução". Somente os processos que estão no estado de execução consomem CPU. O processo de estado de suspensão não consumirá CPU. Eles podem estar consumindo memória, mas essa memória será trocada internamente pelo sistema de memória virtual. Portanto, mesmo se você criar 1000 desses threads, eles não apresentarão muita ameaça ao seu sistema.

Manoj R
fonte