Aumentamos a folga do servidor e ele ficou 20× mais lento
De 10 para 16 requisições simultâneas “para ter margem”. O tempo de resposta dobrou em chamada única e explodiu sob carga.
De 10 para 16 requisições simultâneas “para ter margem”. O tempo de resposta dobrou em chamada única e explodiu sob carga.
O servidor de síntese de voz roda com uma configuração que limita quantas requisições ele processa ao mesmo tempo. Estava em 10. Nós tínhamos folga de memória na placa. A conta parecia trivial: subir para 16 daria margem para picos.
Subimos. E aí:
| Cenário | Com 10 slots | Com 16 slots |
|---|---|---|
| Fator de tempo real, chamada única | 0,21 | 0,44 |
| Primeiro áudio, 8 simultâneas | 156 ms | 3.156 ms |
Dobrou em chamada única, sem concorrência nenhuma. E sob oito chamadas simultâneas, que é o nosso cenário real, o primeiro áudio foi de 156 milissegundos para três segundos. Vinte vezes pior.
Por que mais slots deixam tudo mais lento
Duas coisas acontecem ao mesmo tempo, e nenhuma é óbvia.
A memória é repartida. Os slots dividem o mesmo espaço reservado na placa. Com 16, cada um recebe menos espaço de trabalho, e o modelo passa a fazer mais idas e vindas de memória para caber. Isso sozinho já explica boa parte da degradação.
Os grafos capturados desalinham. Esta é a parte que nos custou o dia. A execução é otimizada capturando antecipadamente a sequência de operações da placa para tamanhos de lote específicos. Quando o número de slots muda, essas capturas deixam de casar com os tamanhos que aparecem na prática, e o caminho rápido, que existia justamente para evitar o custo de despachar cada operação, para de ser usado.
O resultado é um sistema que executa exatamente o mesmo trabalho por um caminho pior. Não há erro em log nenhum. Só fica lento.
O padrão, e ele não é sobre placas de vídeo
Este é um caso particular de uma armadilha geral: aumentar um limite de concorrência não aumenta capacidade quando o recurso disputado é fixo.
É a mesma matemática do pool de conexões de banco que fica mais lento quando você aumenta o número máximo de conexões. É a mesma do servidor web com mil workers numa máquina de quatro núcleos. Em todos os casos, mais permissão para trabalhar em paralelo significa mais disputa pelo mesmo gargalo, mais troca de contexto, e menos trabalho concluído por segundo.
Números que parecem margem de segurança quase sempre são acordos negociados com algum gargalo real. Quando você não sabe qual é o gargalo, aumentar o número não é otimizar, é apostar.
O que fizemos
Voltamos para 10 e escrevemos, no lugar mais visível possível da documentação interna, que aquele número é calibrado e não deve ser mexido “para ter folga”. Com uma tabela. Com os números que medimos.
Porque a próxima pessoa que olhar aquela configuração (e pode ser um de nós, daqui a oito meses) vai ter exatamente a mesma ideia boa que nós tivemos.
O teto real
Com a configuração calibrada, medimos onde a fila realmente começa:
| Simultâneas | Primeiro áudio, mediana | Pior caso |
|---|---|---|
| 4 | 126 a 217 ms | 218 ms |
| 8 | 156 a 310 ms | 311 ms |
| 12 | 150 a 300 ms | 2.193 a 3.669 ms |
Até oito, o pior caso cabe no orçamento de 300 ms. Na décima primeira a fila começa, e o pior caso salta para segundos. É por isso que dizemos “oito conversas simultâneas” em vez de um número redondo mais bonito.
Vale lembrar que a síntese só trabalha enquanto a agente fala. Dez ligações no ar quase nunca são dez sínteses no mesmo instante. E quando forem, a décima primeira não fica muda, ela espera.
Números medidos, não estimados
Todo número que aparece aqui saiu de um teste que a gente refaz quando alguma coisa muda. Se algum estiver errado, queremos saber.