- Refatoração do kernel de Attention para um formato persistent, melhorando o desempenho em comprimentos de contexto baixos
- Em fp16, em contextos grandes, há queda de desempenho devido a um problema de escalonamento de instruções do ptxas na partição de softmax
- Em fp8, foi observado um ganho de cerca de 100 TFLOPS de desempenho quando o nome do kernel inclui "cutlass"
- Essa otimização foi analisada como um truque de otimização hardcoded por meio da nomeação do kernel
- O impacto no desempenho e a causa do comportamento são um fenômeno peculiar causado pela implementação interna do ptxas da NVIDIA
Resumo: Refatoração do kernel Persistent Attention e o efeito da nomenclatura "cutlass"
Visão geral
- Este Pull Request refatora o kernel de attention do Triton para o modelo de persistent attention
- O principal objetivo é obter otimização de desempenho em faixas de comprimento de contexto baixas
- Porém, no caso do tipo fp16, conforme o contexto aumenta, surge uma queda de desempenho devido a um problema de escalonamento de instruções do ptxas na parte de softmax
Efeito de adotar o nome "cutlass" em fp8
- Ao usar modelos fp8, foi medido um ganho de desempenho de até cerca de 100 TFLOPS quando "cutlass" é incluído no nome do kernel
- Isso acontece porque o ptxas (montador PTX) da NVIDIA aplica internamente uma otimização especial quando o nome do kernel contém "cutlass"
- No código real, quando o dtype é float8e5, o nome do kernel é atribuído como algo como "cutlass_gluon_attention"
- A análise do disassembly do ptxas mostrou que, de fato, existe uma condição
strstr(kernel_name, "cutlass") no código interno
Benchmark de desempenho
- Nos dados de benchmark antes e depois da refatoração, foi observada uma queda relativa de desempenho em D=64 em comparação com antes da aplicação de persistent attention
- Isso é atribuído ao problema de escalonamento de instruções na parte de softmax
- Porém, ao combinar fp8 com a nomenclatura "cutlass", em certos contextos e tamanhos foi registrado um throughput significativamente mais alto
- Resumo de resultados representativos:
- Attention Z=4, H=32, D=64, causal=False:
- antes de aplicar persistent: triton-fp16 cerca de 383, triton-fp8 cerca de 413, cudnn-fp16 cerca de 565
- depois de aplicar persistent: triton-fp16 cerca de 360, triton-fp8 cerca de 370
- Attention Z=4, H=32, D=128, causal=True:
- antes de aplicar persistent: triton-fp16 cerca de 312, triton-fp8 cerca de 345, cudnn-fp16 cerca de 553
- depois de aplicar persistent: triton-fp16 cerca de 356, triton-fp8 cerca de 351
Descoberta e análise do truque de nomenclatura
- Análises da comunidade confirmaram que, quando a string
"cutlass" está incluída no nome do kernel, uma rotina experimental de otimização é ativada no NVIDIA ptxas
- O ptxas contém lógica hardcoded para casar nomes (
strstr(kernel_name, "cutlass"))
- Esse truque é uma otimização agressiva e experimental, e se há mudança na exatidão do kernel pode variar conforme o hardware e a situação
Discussão na comunidade
- Vários revisores levantaram perguntas e análises sobre comparação de desempenho, exatidão e método de otimização
- Também foram discutidos pontos como o conteúdo do relatório técnico da Deepseek e diferenças em arquiteturas específicas, como GPUs Hopper
- Também surgiram questões sobre o quão amplamente essa otimização via mudança de nome se aplica e sobre possíveis problemas de estabilidade
Conclusão e implicações
- É um fenômeno bastante incomum que apenas o nome do kernel possa causar mudanças reais de desempenho no nível de hardware
- O fato de existir um gatilho de otimização oculto na stack de software da NVIDIA (pxtas) sugere que, no desenvolvimento de frameworks de IA, as convenções de nomenclatura também podem afetar o desempenho
- Na prática, ao usar esse truque, é essencial testar reprodutibilidade e estabilidade, além de acompanhar de perto as políticas de otimização do fornecedor de hardware
1 comentários
Comentários no Hacker News
Ao desmontar o
ptxas, confirmaram que existe lógica hardcoded para detectar nomes de kernel contendo "cutlass"Parece que a NVIDIA aplicou aqui uma otimização instável, experimental e agressiva; se essa otimização fosse sempre ativada incondicionalmente, poderia causar bugs sutis
Compiladores de GPU são extremamente difíceis, e sempre que se tenta ir além das otimizações básicas, os resultados costumam vir misturados com problemas
Alguns kernels ficam mais rápidos e outros mais lentos, e é muito difícil conseguir um ganho geral de desempenho
Quase nunca uma única otimização aumenta consistentemente a velocidade em toda a bateria de testes
Minha experiência é com sistemas de GPU que não são da Nvidia, mas a situação parece bem familiar
A Nvidia provavelmente também encontrou uma otimização que produz resultados incríveis para alguns conjuntos de kernels e resultados péssimos para outros, e aparentemente não conseguiu achar um critério confiável para aplicá-la automaticamente
Obrigado pelo contexto
Não sou dessa área (e é a primeira vez que ouço falar desse projeto), então eu realmente não estava entendendo o título nem o conteúdo do PR
Isso me lembrou de quando a ATI (AMD), há 25 anos, foi pega manipulando benchmark no Quake III porque o desempenho mudava ao renomear o executável para
quackTambém deixaram links relacionados: review da techreport.com, review da hardocp.com, artigo da 3dcenter.de
Só para esclarecer, caso alguém tenha entendido errado como eu
A ATI detectava o nome de executável
quakee alterava a qualidade das texturas etc. para inflar a pontuação do benchmarkAs pessoas perceberam isso ao renomear o executável para
quack: a qualidade da imagem aumentava e a pontuação caía, mostrando que o driver da ATI estava reduzindo deliberadamente a qualidade para ganhar velocidadeOu seja, não foi a ATI que mudou o nome do arquivo; foram os usuários
Ou então aquela questão em que o Intel C++ Compiler verificava a string
GenuineIntelna saídaO link da wiki está aqui
Até hoje todos os vendors fazem esse tipo de coisa
O driver intercepta o loop de renderização de jogos populares para corrigir bugs, substituir shaders por versões mais otimizadas ou liberar caminhos rápidos de execução, entre várias outras intervenções
Essas mudanças deveriam ter impacto mínimo no resultado visual, mas em alguns casos a intervenção é agressiva demais e a qualidade cai bastante
Tudo isso para fazer o jogo rodar mais rápido no hardware deles
Há uma thread com discussão mais profunda sobre esse tema; recomendo esta aqui
Curiosamente, é um post anterior sobre a mesma otimização relacionada ao cutlass
Esse tipo de caso é muito comum
Fabricantes de chipsets para celulares trapacearam em benchmarks (VW nas emissões, Nvidia no benchmark 3DMark, Intel nos benchmarks SPEC para Xeon etc.)
Em computação gráfica, já virou quase costume os drivers incluírem tweaks, configurações e workarounds específicos para cada jogo
(Infelizmente, hoje em dia links de fontes confiáveis somem com tanta frequência que é preciso usar o archive.org. Acho importante preservar esse tipo de memória.)
Links de referência: manipulação de benchmark pela Mediatek, escândalo das emissões da Volkswagen, polêmica do Nvidia 3DMark, impacto do compilador da Intel em benchmarks SPEC
Do ponto de vista de quem trabalha com compiladores, às vezes esse tipo de otimização acaba dependendo de nomes (
schema, substring etc.)Mesmo que isso não agrade, na prática é assim que funciona
Nem sempre é algo malicioso; projetar a otimização para atingir só a própria biblioteca pode ser uma escolha mais segura do que arriscar quebrar tudo
Ou então isso acontece porque o frontend não consegue fornecer informações mais confiáveis, como informações estruturais
Não acho que esse tipo de abordagem ajude
Não acho que haja muita novidade nisso
Isso me lembrou de quando, cerca de 10 anos atrás, em uma certa versão do Webpack, usar o nome de arquivo
add.svgquebrava o buildNo fim, o problema foi resolvido trocando o nome do arquivo para
plus.svgnumpytambém é parecidoTem gente que já acabou quebrando o
numpysem querer ao usar um arquivo chamadosecret.pyAcho impressionante como a mensagem do commit é honesta
Houve crítica sobre a forma como o diff do commit foi organizado
Alguém escreve “tornou alguma coisa cerca de 100 tflops mais rápida”, e mesmo assim a reação é dizer que “a mensagem do commit é ruim”… nesse caso, imagino que essa pessoa também não gostaria do estilo de commits do John Carmack
Pessoalmente, eu gosto desse tipo de mensagem sincera
Acho muito melhor do que uma sequência interminável de mensagens de commit geradas por IA, do tipo "refactored X"
Acho que seria melhor fazer squash e consolidar o histórico de commits
Não entendo muito bem por que não fizeram squash ao subir no GitHub
Isso me lembrou de quando aprendi a usar o NVIDIA Jetson
Descobri que bastava executar um único comando para tudo ficar mais rápido (link relacionado)
Pelo artigo, existem dois modos, 5W e 10W, e como 10W é o padrão, isso até pode ser interpretado como “fica mais rápido” só quando você muda de 5W de volta para o padrão. Talvez eu tenha entendido algo errado
Às vezes se usa código altamente ajustado em linguagem de alto nível (C++ etc.) e ainda assim se espera um resultado específico no assembly de GPU, mas o compilador não gera o que se queria
Se você falar com a equipe do fornecedor, talvez ofereçam várias soluções, mas em código open source muitas delas são difíceis de aplicar (por exemplo,
#pragmaproprietário, intrinsic etc.)Para quem constrói bibliotecas de alto desempenho, não atingir a performance esperada pode significar que o produto simplesmente não pode ser lançado
Nesses casos, acaba sendo necessário recorrer a truques como induzir certas transformações de código por meio do nome da função
Esse tipo de otimização é comum no mundo real e, na minha opinião, não está no mesmo nível de reduzir a qualidade da imagem para trapacear benchmark
Esse assunto já tinha sido discutido quando esse PR foi aberto pela primeira vez
Não parece haver nada de novo
Discussão anterior
Eu gostaria que existisse uma estrutura econômica em que compartilhar código fosse fácil
Em vez de drivers com binary blobs, baseband e outras estruturas complexas, seria bom se todos pudessem acessar facilmente a informação e reduzir o desperdício de tempo e esforço
Inúmeros engenheiros e pesquisadores brilhantes passam meses ou anos fazendo engenharia reversa e decifrando documentação, esquemas e código binário que alguém já possui
Empresas como CUDA e NVIDIA são frustrantes nesse sentido
No caso de aplicativos desktop, o custo adicional por usuário tende praticamente a zero
Por isso, software precisa de uma estrutura de receita recorrente, mas essa receita não pode continuar crescendo proporcionalmente ao número de usuários
Quem investiu no desenvolvimento de código novo precisa recuperar esse investimento quando o produto se tornar popular, mas o crowdfunding tradicional não atende bem a essa estrutura
À medida que o número de usuários cresce, a contribuição por pessoa cai drasticamente; mesmo que todos contribuam com 100%, 10% ou até 1%, o montante arrecadado ainda pode ser astronômico
No fim, acho que seria necessário um sistema de promessas no estilo leilão (
pledge)Esse modelo também tem problemas, especialmente na transição entre modos exclusivos e não exclusivos
Quando algumas grandes empresas bancam todo o custo de desenvolvimento e depois o resultado é aberto como open source, todas as outras pessoas e empresas acabam, na prática, como free riders