Otimizar a carga de trabalho real do Snake
O caminho opt-in adotado combina compilação MLX, buckets de comprimento de 16 tokens e uma cache limitada de prefixos de perguntas tokenizadas. Não altera os pesos, não quantiza o modelo, não faz cache de previsões nem reutiliza estados ocultos do codificador bidirecional entre perguntas.
Num teste emparelhado de ciclo completo, o caminho otimizado fornecido alcançou 75.40 movimentos/segundo ao longo de 2,400 movimentos, contra 70.82 movimentos/segundo para a inferência eager no mesmo teste: 1.065×, ou cerca de 6.5%. Ambos tiveram zero mortes, 2 intervenções de segurança e ações executadas idênticas em 2,400/2,400 passos. O crescimento combinado da memória ativa do MLX após o warmup e a limpeza da cache foi 0 bytes.
Ativá-lo
laya-snake --optimize
laya-snake --optimize --max-speed
A API geral expõe os mesmos controlos opt-in:
import laya_mlx as laya
agent = laya.load(
"aac6fef/laya-multilingual-mlx",
compile=True,
pad_to_multiple=16,
cache_prompts=True,
)
As três opções estão desativadas por predefinição, preservando o comportamento eager existente e a configuração do benchmark. A compilação especializa-se para a forma de entrada; o primeiro uso e as novas formas podem implicar custos de compilação. Constrói um novo Agent depois de alterar os pesos ou a estrutura dos módulos. O padding arredonda o comprimento da sequência para o múltiplo pedido sem exceder o limite de contexto configurado. As máscaras excluem os tokens de padding.
cache_prompts=True mantém no máximo 128 prefixos imutáveis de PreparedQuestion por Agent, incluindo posições de marcadores. As chaves de cache incluem a identidade do tokenizer, os tokens especiais, o tipo de pergunta, as opções renderizadas ordenadas, as instruções e o orçamento do prefixo. O estado é sanitizado e tokenizado uma vez por chamada a prepare, depois concatenado de forma independente com cada prefixo de pergunta. As alterações de pergunta criam ou selecionam o prefixo adequado. Cada pergunta continua a ter uma passagem direta completa do modelo.
Ablação de forma e preparação
Esta demonstração faz três perguntas por movimento: direção, estimativa de rota segura e estimativa de acessibilidade da comida. Assim, o seu tamanho de lote é 3, e não 1. Em 32 tabuleiros reais gravados e amostrados:
- Os comprimentos de sequência multilingues eram 59, 61, 63 e 64. Um múltiplo de 16 tokens coloca-os todos num bucket de 64 tokens.
- Os comprimentos de sequência em inglês eram 66, 68, 69 e 70, correspondendo a um bucket de 80 tokens.
- Fazer padding da entrada multilingue de 64 para 96 acrescenta 50% ao trabalho por token; não é o pequeno ajuste de 93 para 96 sugerido pelo fixture separado de texto curto da API.
Os candidatos correram em ordem rotativa dentro de cada estado idêntico, depois de visitarem cada forma medida uma vez. A tabela contém a latência sincronizada de Agent.predict, incluindo tokenização e conversão da saída, e exclui o trabalho do planeador/UI e o warmup inicial da forma.
| Variante | Multilingual p50 / p95 (ms) | Inglês p50 / p95 (ms) |
|---|---|---|
| Eager | 9.12 / 10.21 | 21.83 / 26.73 |
| Apenas reutilização de prefixo | 8.95 / 9.75 | 21.60 / 25.23 |
| Compilado, comprimento real | 8.67 / 9.66 | 21.27 / 25.99 |
| Compilado, padding para 96 | 10.92 / 11.72 | 25.66 / 29.88 |
| Compilado + reutilização de prefixo | 8.66 / 9.21 | 21.03 / 23.97 |
| Compilado, bucket da carga de trabalho | 8.66 / 9.55 | 21.78 / 26.12 |
| Compilado + bucket + reutilização de prefixo | 8.56 / 9.29 | 21.51 / 24.16 |
Os sete candidatos corresponderam às direções propostas e executadas em regime eager em 32/32 tabuleiros por checkpoint. A diferença máxima nas probabilidades e estimativas apresentadas, com quatro casas decimais, foi 0 nestas amostras. Isto é uma concordância de saída arredondada numa amostra finita, não uma afirmação de tensores internos de vírgula flutuante idênticos bit a bit.
A ablação usou wrappers limitados de preparação de prefixos para triar designs. O teste de ciclo completo abaixo usa a implementação real da API compile, pad_to_multiple e cache_prompts fornecida. Os seus testes de correção comparam adicionalmente os IDs e marcadores preparados sob truncagem de estado, critérios em mudança, saneamento de máscaras e remoção de entradas da cache.
O caminho otimizado fornecido também passou a matriz completa de validação com checkpoints reais: 63/63 de concordância de resposta selecionada para cada um dos três checkpoints em FP32 e FP16 (378/378 no total). Os erros de probabilidade calibrada mantiveram-se dentro das tolerâncias existentes. Cada configuração passou 10 chamadas repetidas, finitas e determinísticas adicionais, com 0 bytes de crescimento da memória ativa medido. Dados de validação otimizados. Os resultados de 100 repetições por configuração do caminho eager original permanecem no relatório de benchmark original.
Para o inglês, a compilação com o comprimento de sequência real foi melhor do que forçar o bucket maior nesta amostra. A demonstração usa o multilingue por predefinição; os utilizadores da API geral podem deixar pad_to_multiple=None enquanto ativam a compilação e a reutilização de prefixo.
Teste emparelhado de ciclo completo
Quatro seeds, 600 movimentos cada, com a ordem dos candidatos a alternar por seed. A renderização inclui composição Rich truecolor e serialização ANSI, e exclui a pintura do emulador de terminal. Cada movimento realiza uma nova previsão. Os resultados são de uma execução emparelhada local.
| Seed | Movimentos/s eager | Movimentos/s otimizado | Pontuação (ambos) | Concordância de ações |
|---|---|---|---|---|
| 101 | 68.60 | 78.04 | 20 | 600 |
| 102 | 70.07 | 78.62 | 24 | 600 |
| 103 | 76.75 | 85.62 | 23 | 600 |
| 104 | 68.50 | 63.15 | 16 | 600 |
O caminho otimizado foi mais lento numa seed. Por conseguinte, 6.5% é a melhoria combinada nesta execução medida, e não uma melhoria garantida para todos os episódios ou máquinas. A varredura ampla de velocidade anterior e este teste emparelhado posterior são execuções diferentes; as suas taxas absolutas não devem ser subtraídas para reivindicar um aumento de velocidade. Dados completos do ciclo.
Escolher o modelo pela jogabilidade e também pela latência
Ambos os checkpoints correram 20 seeds emparelhadas × 300 movimentos, com a ordem dos checkpoints a alternar a cada seed. Cada episódio usou o mesmo estado inicial, a mesma seed de RNG da comida, as mesmas descrições compactas de características e o mesmo escudo de ciclo. O horizonte é fixo; estas são pontuações após 300 movimentos, não jogos completos terminados em morte ou num tabuleiro cheio. Não foi incluída qualquer renderização de terminal nesta comparação de modelos.
| Checkpoint | Sobreviveu / episódios | Movimentos | Pontuação mediana / média | Inferência p50 / p95 (ms) | Intervenções |
|---|---|---|---|---|---|
| laya | 20 / 20 | 6000 | 7.0 / 6.9 | 23.15 / 28.21 | 0 |
| multilingual | 20 / 20 | 6000 | 10.0 / 9.9 | 9.38 / 14.38 | 2 |
O multilingue fez mais progresso na comida e foi mais rápido nesta carga de trabalho, por isso continua a ser o checkpoint predefinido da demonstração. O resultado avalia esta política assistida por características, não a qualidade de raciocínio geral nem um modelo Snake sem ajuda. Todos os episódios e inferências.
Reproduzir
uv run --extra demo python -m experiments.snake_runtime \
--output artifacts/snake/runtime-multilingual.json
uv run --extra demo python -m experiments.snake_runtime \
--model models/hub/laya-mlx --bucket 80 \
--output artifacts/snake/runtime-english.json
uv run --extra demo python -m benchmarks.snake_optimized \
--output artifacts/snake/optimized-paired.json
uv run --extra demo python -m benchmarks.snake_models \
--episodes 20 --steps 300 --output artifacts/snake/models.json
Executa as medições da GPU sequencialmente. Descarrega primeiro os dois diretórios de modelos locais. A gravação de origem incluída no repositório fornece os estados exatos dos tabuleiros amostrados. Ablações em bruto: multilingual, inglês, piloto inicial de 96 tokens.
A anterior comparação entre prompt compacto e detalhado alternou a ordem dos prompts em 64 estados e encontrou uma mediana de 11.80 → 9.29 ms depois de descartar as primeiras 8 iterações de warmup. O seu registo ad-hoc original não guardou instantâneos do tabuleiro, por isso é evidência de apoio e não a ablação reproduzível principal. python -m benchmarks.snake_prompt fornece uma versão reproduzível que guarda estados, seed, decisões completas e método.
A implementação segue o guia oficial de compilação do MLX: usa um callable compilado de longa duração e especialização de forma normal. Não usa shapeless=True em código de modelo Python dependente da forma. A documentação atual foi verificada através do site oficial depois de os pedidos ao CLI do Context7 terem falhado com erros de rede.