1 pontos por GN⁺ 2024-07-14 | 1 comentários | Compartilhar no WhatsApp
  • Como as verificações periódicas de RAID do servidor de tablebase Syzygy de 7 peças estavam ficando para trás por causa da carga de requisições no Lichess, a equipe trocou o varrimento completo de blocos por verificação de integridade na leitura
  • Para migrar a tablebase de 17TiB sem horas de indisponibilidade, prepararam um novo servidor e validaram a configuração reproduzindo logs reais de requisições em um ambiente com 32GiB de RAM, 2×201GiB NVMe e 6×5.46TiB HDD
  • Ao reproduzir 1 milhão de requisições registradas em produção com 12 clientes em paralelo, ficou claro que o principal gargalo não era a resposta média, mas sim a latência de cauda percebida pelo usuário
  • Do ponto de vista de implementação, pread(2) se saiu melhor que mmap em tratamento de erros e latência de cauda, e dicas de acesso aleatório como POSIX_FADV_RANDOM e MADV_RANDOM em geral tiveram efeito contrário
  • Com SSD limitado, colocaram nele os prefixos das tables e paralelizaram os probes internos de cada requisição para reduzir acessos lentos ao disco, além de verificar se as melhorias do benchmark também apareciam no tempo de resposta em produção

Mudança de verificação total do RAID para validação na leitura

  • O servidor de tablebase Syzygy de 7 peças do Lichess tinha dificuldade para concluir as verificações periódicas de integridade do RAID enquanto recebia muitas requisições de tablebase
  • A nova configuração usa dm-integrity on LVM para não verificar periodicamente todos os blocos de dados, validando cada bloco sempre que ele é lido
  • Para migrar a tablebase de 17TiB sem várias horas de indisponibilidade, foi montado um novo servidor separado
    • Isso permitiu executar benchmarks controlados sobre toda a tablebase antes da troca real
    • Depois disso, o serviço foi migrado para o novo servidor e o antigo foi aposentado

Configuração do novo servidor

  • A RAM foi mantida em 32GiB, igual à do servidor anterior
  • No armazenamento, foram adicionados 2×201GiB NVMe, que não existiam no servidor anterior, e o espaço restante do disco de 476GiB foi reservado para o sistema operacional e área de trabalho
  • Os HDDs passaram de 5 para 6×5.46TiB HDD
  • O sistema operacional é Debian bookworm, com kernel da linha Linux 6.1.0-21-amd64
  • O escalonador de I/O padrão selecionado era none no NVMe e mq-deadline nos HDDs

Configuração de RAID 5 e monitoramento

  • O RAID 5 é adequado para o servidor de tablebase porque pode se recuperar de falha em um único disco e distribuir leituras aleatórias entre vários discos
  • A configuração inicial era a seguinte
lvcreate --type raid5 --raidintegrity y --raidintegrityblocksize 512 --name tables --size 21T vg-hdd
  • O desempenho inicial dos testes era bom, mas sem monitoramento seria fácil deixar passar o problema de alguns discos não participarem no mesmo nível que os outros
  • Se --stripes for omitido, nem todos os volumes físicos são usados por padrão
  • O monitoramento da atividade de leitura por disco foi necessário para detectar a configuração incorreta do RAID

Gargalos observados em logs reais de requisição

  • Em condições normais, o servidor recebe entre 10 e 35 requisições por segundo
  • Foram registrados 1 milhão de requisições em produção, e no cenário escolhido 12 clientes em paralelo as reenviaram em sequência
  • As tables são abertas de forma lazy, e os caches da aplicação e do sistema operacional são preenchidos gradualmente
    • Os tempos de resposta das primeiras 800 mil foram excluídos como aquecimento
    • Em seguida, foram analisados os tempos de resposta das 200 mil requisições restantes
  • O tempo médio de resposta é suficientemente rápido, mas a latência de cauda alta virou o foco da otimização
  • O gráfico ECDF mostra a proporção de requisições mais rápidas que cada tempo de resposta, e o eixo x está em escala logarítmica
  • No gráfico, foram somados 30ms a cada tempo de resposta para refletir o ping time de 30ms do cliente
    • Isso evita que a escala logarítmica do eixo x exagere diferenças de poucos milissegundos na faixa mais baixa

pread(2) teve vantagem sobre mmap

  • A implementação Syzygy tablebase shakmaty-syzygy oferece uma interface que permite trocar tanto a forma de abrir os arquivos de table quanto a forma de leitura
  • Havia dois candidatos principais
    • mmap: mapeia o arquivo da table na memória, e as leituras de disco acontecem de forma transparente ao acessar essa região
    • pread(2): faz uma system call a cada leitura e informa erros de leitura pelo valor de retorno
  • O mmap não exige system calls adicionais depois do mapeamento, mas como a leitura se parece com um acesso comum à memória, erros precisam ser tratados por mecanismos fora de banda, como sinais
  • Na implementação do servidor, só o tratamento de erros mais robusto já justificava usar pread, e os benchmarks também mostraram melhor desempenho do pread nos cenários de interesse
  • Uma possível causa é que o acesso a um único bloco de dados mapeado em memória pode cruzar o limite de página e acabar exigindo duas leituras de disco
  • Isso não significa que seja preciso aplicar pread imediatamente a motores de xadrez
    • Em partidas entre engines, o uso de tablebase normalmente acontece quando todas as WDL tables podem ficar em um dispositivo de armazenamento suficientemente rápido
    • Nesse caso, a faixa típica de tempo de resposta nem aparece no gráfico, e o mapeamento de memória é melhor por reduzir o overhead de system calls

Efeito contrário das dicas de acesso aleatório

  • posix_fadvise(fd, 0, 0, POSIX_FADV_RANDOM) e a dica equivalente para memory map acabaram tendo, na maioria dos casos, efeito negativo
  • POSIX_FADV_RANDOM informa ao sistema operacional que o acesso ao arquivo é aleatório e que o read-ahead automático provavelmente não será útil, servindo como dica para reduzir pressão no page cache
  • O padrão de acesso à tablebase quando pessoas analisam finais pode ser menos aleatório do que se imaginava
  • Em engines de xadrez, os probes podem ficar mais espalhados entre finais diferentes, então o resultado pode mudar

Prefixos das tables colocados em SSD limitado

  • Um probe na table primeiro codifica a posição como um índice inteiro com base nas informações de codificação do cabeçalho da table
  • Depois, é preciso localizar o bloco de dados comprimido que contém o resultado desse índice
  • O Syzygy fornece uma lista esparsa de comprimentos de blocos que aponta para perto da entrada correta, e em seguida usa a lista de comprimentos de blocos para localizar o bloco de dados relevante
  • Os tamanhos das seções da table são os seguintes
Table section WDL DTZ Total
Headers and sparse block length lists 38GiB 9GiB 47GiB
Block length lists 274GiB 64GiB 339GiB
Compressed data blocks 8433GiB 8458GiB 16891GiB
  • Também seria possível usar o espaço em SSD como uma camada de cache adaptativa para armazenar entradas de lista frequentes e blocos de dados quentes
  • Como o objetivo era reduzir a latência de cauda, fazia mais sentido considerar o pior caso e colocar no SSD a lista esparsa de comprimentos de blocos e a lista de comprimentos de blocos
  • Com esse arranjo, independentemente de a table estar quente ou fria, cada probe na table fica limitado a no máximo 1 acesso lento ao disco
  • Nesse servidor, não havia espaço suficiente em SSD para fazer espelhamento RAID 1, e como era uma otimização seletiva, a redundância foi sacrificada em favor de RAID 0

Paralelização dos probes internos de cada requisição

  • Uma requisição típica de tablebase feita por um motor de xadrez pede um único valor WDL
  • Na interface para o usuário, a intenção é mostrar os valores DTZ para todos os lances
  • Incluindo a resolução interna de capturas no Syzygy, uma requisição média gera 23 probes WDL e 70 probes DTZ
  • A implementação inicial paralelizava o processamento entre requisições, mas executava em sequência os probes dentro de cada requisição
  • Uma paralelização mais fina aumenta o overhead na faixa de menor latência, mas reduz bastante a latência de cauda
  • Mesmo que o disco não consiga processar fisicamente muitas leituras paralelas de verdade, o escalonador de I/O passa a ter mais chances de planejar as leituras para concluir cada requisição mais rápido
  • Isso ajuda a ordenar melhor os acessos relacionados ao disco para reduzir o tempo até que a cabeça do disco alcance o setor necessário para a próxima requisição

Verificação em produção e dados brutos

  • Foi usado um gráfico de tempos de resposta para verificar se as otimizações do cenário de benchmark também ajudavam no ambiente real de produção
  • Os dados brutos estão publicados em lila-tablebase-bench

1 comentários

 
GN⁺ 2024-07-14
Comentários no Hacker News
  • Lichess é um serviço que dá vontade de admirar em silêncio, como um bom vinho. É realmente excelente para a comunidade de xadrez, e uso todos os dias continuando impressionado com os recursos e o desempenho
    Fica ainda mais surpreendente quando se sabe que é uma equipe de apenas 1 a 2 pessoas com orçamento limitado

    • Também não dá para esquecer que é gratuito, open source, não fica pedindo dinheiro e nem vai fazer isso no futuro. Muita gente doa, os gastos são públicos e também existe aplicativo
    • Queria que mais softwares open source voltados ao usuário final fossem como o Lichess: amigáveis, bem projetados e bem mantidos
    • Sinto o mesmo. O novo app móvel beta recente está mais limpo e ainda tem feedback tátil, o que é muito legal
    • Um dia eu queria criar algo tão valioso e incrível quanto o Lichess
  • Achei interessante a parte que mostrava a ECDF com 30 ms adicionados a cada tempo de resposta
    Adicionar uma constante pode parecer artificial, mas na prática é uma forma de ver o resultado da perspectiva de um cliente com ping de 30 ms, e o eixo x em escala logarítmica evita exagerar diferenças de alguns milissegundos nas faixas mais baixas. Talvez seja uma técnica padrão, mas pareceu um truque bem inteligente

  • Fiquei me perguntando se realmente era necessário cortar custos, ou se não havia outro motivo além disso para simplesmente colocar um SSD de 20 TB em uma máquina e encerrar o assunto. Até SSDs de 4 TB custam algo como 300 dólares, e drives SFF da HP ou Dell também não são muito mais caros
    Provavelmente havia interesse no teste e na otimização em si, e do ponto de vista de produto talvez o tempo limitado tivesse sido melhor gasto em outros projetos

    • O Lichess é sem fins lucrativos e tem muitos voluntários, então é bem provável que o equilíbrio entre custo de hardware e tempo seja diferente do da maioria das empresas com fins lucrativos
    • O Lichess é uma organização sem fins lucrativos operada apenas com doações e voluntariado. O único funcionário é a pessoa que criou a organização sem fins lucrativos, e parece receber muito menos do que poderia ganhar em outro lugar dado o nível de habilidade
      Não sei como o fato de a organização ser baseada na França afeta os custos, mas vale mencionar
    • Com esse trabalho, o tempo máximo de resposta caiu para a faixa de um dígito. Se o projeto levou uma semana, no máximo duas, e reduziu o tempo de resposta de alguns usuários de 15 segundos para 1,5 segundo, então já valeu muito a pena
      Para justificar melhor o investimento de tempo do que isso, só se houvesse outro projeto com experiência de usuário ainda pior, ou se fosse uma organização com fins lucrativos que tivesse oportunidades de ganhar dinheiro em outro lugar e admitisse não ligar muito para o sofrimento do cliente
    • Acho que quase não existe em outras indústrias além de TI esse tipo de engenheiro que pensa em “testar e otimizar por diversão”
      Parece resultado da combinação entre hardware poderoso e barato demais e gente preguiçosa que quer apenas dizer “por hoje chega”. Não existe também aquela ideia de ter orgulho do próprio trabalho?
    • Faz sentido do ponto de vista de produto, mas o Lichess não é uma empresa com fins lucrativos com um produto, e sim uma organização realmente sem fins lucrativos, então é preciso mudar a perspectiva para entender a decisão
  • Há algumas escolhas questionáveis nessa otimização. O motivo da otimização é que havia atividade de I/O demais e por isso a verificação de RAID não conseguia terminar
    Pelo texto, não fica claro se a verificação de RAID sobre 17 TiB de dados já chegou a terminar alguma vez. Em vez disso, desativaram a verificação periódica de RAID e mudaram para checagem de erros por página ao ler os dados, mas as duas coisas não são equivalentes e, se os dados forem importantes, o ideal é usar ambas
    Se a corrupção só for descoberta quando alguém tentar ler os dados, danos antigos podem continuar lá por muito tempo e talvez o original já não possa mais ser recuperado porque o prazo de retenção do backup passou. Também está implícito aqui que mudaram para RAID 0, que é de fato a opção mais rápida, mas isso pressupõe uma confiança considerável de que essa configuração de NVMe vai aguentar essa carga
    Espero que os backups estejam bem feitos. Uma boa solução seria subir um servidor temporário, restaurar o backup e fazer uma verificação completa dos dados, e se der certo ainda validar junto os procedimentos de backup e restauração e a integridade dos arquivos. Mesmo assim, ainda deveria haver folga para concluir a verificação de RAID no servidor principal, e é melhor evitar usar RAID 0 por causa de desempenho

    • É verdade que as duas abordagens não são iguais, mas para este caso de uso isso basta. Se detectarem corrupção dos dados, podem simplesmente descartar o arquivo e baixá-lo novamente ou regenerá-lo
      Trata-se de um conjunto de dados disponível livremente, só que grande. https://en.wikipedia.org/wiki/Endgame_tablebase explica melhor. Por isso também não fazem backup
  • Também existe o lishogi, mas ainda é pequeno demais para precisar desse tipo de otimização
    Entre as variantes de xadrez, shogi é a mais divertida, enquanto xiangqi nem tanto

  • Fiquei pensando se lichess significaria algo como lich feminino, tipo baron/baroness

    • Títulos de nobreza não são um bom comparativo. São um dos raros casos em que realmente existe uma raiz exclusiva masculina; na maioria das palavras a raiz é neutra, e mesmo quando há forma masculina ou feminina costuma ser preciso um sufixo
      Tecnicamente, lich masculino seria “werlich”, lich feminino seria “wiflich”, e no plural recebe “-en”. Mas como para mortos-vivos o gênero geralmente é irrelevante, a forma neutra é de longe a mais usada
      “lichess” é uma combinação estranha que mistura raízes do alemão e do francês, então naturalmente não se distingue de outras palavras do inglês
    • Significa Libre chess, ou seja, xadrez livre e open source
  • Não é uma comparação totalmente justa, mas a qualidade de engenharia da equipe do Lichess realmente impressiona. O principal concorrente se gabava da migração para a GCP e ainda assim sofreu interrupções repetidas com o aumento da popularidade, tendo provavelmente umas 100 vezes mais gente
    O ponto fraco do Lichess era o app móvel, mas a v2 refeita em Flutter, mesmo ainda em beta, já é bem boa
    E também vale lembrar que o Thibault recebe menos de 60 mil dólares por ano como remuneração

    • Não acho que ele devesse se sentir culpado por aumentar o próprio salário. Subir para 200 mil dólares por ano e tornar a vida mais confortável só pode fazer bem para o projeto no longo prazo
    • O Lichess é um serviço excelente para jogadores casuais de xadrez como eu jogarem rapidamente uma partida com outra pessoa. Quase nunca tem espera
      Só fico curioso sobre como se pronuncia Lichess. É Lie chess, Le chess ou League chess?
    • O Lichess parece um ótimo exemplo de quão eficiente a Wikipedia poderia ter sido tanto em código quanto em organização
    • Acho que você está superestimando bastante quantos desenvolvedores a Chess.com tem