Nada roda até você pedir. Nenhum contexto WebGL2 é criado ao abrir
a página: o canvas nasce no clique e o código é buscado de
/shaders-gpu. Um único contexto compartilhado serve a página inteira, então
um shader anima por vez. Rodar outro devolve o atual ao estado inicial.
A distinção abaixo é a mesma que uso no resto do site. Nenhuma linha aqui diz "OpenCL é mais rápido" — porque eu não medi isso, e é a parte que está em aberto.
| Afirmação | Força | Por quê |
|---|---|---|
| Toda GPU NVIDIA expõe um dispositivo OpenCL | EXACT | Garantido pelo driver; checklist de compatibilidade da Khronos |
| Existem GPUs e aceleradores com OpenCL e sem CUDA | EXACT | AMD, Intel, Apple, FPGA, CPU — nenhum roda CUDA |
| O mesmo kernel em GLSL/OpenCL C/CUDA é o mesmo texto | EXACT | Verdadeiro para aritmética; falso assim que entra biblioteca |
| O SIMT de CUDA é mais expressivo que OpenCL 1.2 | EXACT | Shuffle/ballot nativos; OpenCL só teve subgroups no 2.0 |
| OpenCL é competitivo em desempenho com CUDA | OPEN | Depende do kernel e do driver; eu não medi |
| Meu M22 mediu GPU além de NVIDIA | FALSO | torch.cuda.is_available() é a única detecção |
EXACT verificável na documentação · OPEN em aberto — não medi · FALSO afirmação que eu não posso fazer
benchmark.py a detecção de hardware é literalmente
gpu_available = torch.cuda.is_available(), e os backends medidos são
["cpu"], "cuda" e "mps". Não existe uma linha de
OpenCL no meu código. Quando eu escrevo "GPU" numa tabela do meu relatório, leia
"GPU NVIDIA". O argumento desta página é sobre API e ecossistema — não é, e não pode
ser, uma reivindicação de desempenho meu.
A Cadeia CUDA: fonte → PTX → SASS
Do lado de fora, o que você escreve. No meio, o PTX — ISA virtual, só NVIDIA. No centro, o SASS: ISA real, não documentada, específica de cada arquitetura.
Três anéis. O externo é o que você escreve: CUDA C++, regular e legível. O médio é o PTX, a ISA virtual da NVIDIA — ainda regular, mas já só dela. O núcleo é o SASS: a ISA real, não documentada, que muda a cada arquitetura. A cada volta do relógio o núcleo se remexe: é a arquitetura mudando por baixo do seu código.
O que a imagem não mostra é o que acontece na fonte: PTX é compatível para frente entre arquiteturas NVIDIA, então seu binário PTX sobrevive a uma troca de placa — mas não sobrevive a uma troca de fornecedor. A cadeia é fechada na primeira camada, não na última. Não estou dizendo que isso é ilegal ou errado: é uma escolha de engenharia da NVIDIA, e ela é o motivo pelo qual o ecossistema funciona.
A imagem custa três máscaras de raio e um hash. O argumento custa uma linha de especificação: SASS não é público.
shaders-gpu/cadeia-ptx.glsl.
Alcance: CUDA × OpenCL
Cada célula é um dispositivo. Aceso = o kernel roda nela. O slider atravessa de CUDA (só NVIDIA) para OpenCL (GPU, CPU, FPGA, acelerador).
Uma malha de dispositivos. Cada célula é uma peça de hardware que existe hoje num datacenter ou numa mesa. O brilho é se o seu kernel consegue rodar nela: verde é NVIDIA, vermelho AMD, azul Intel, branco Apple, amarelo FPGA, violeta CPU, ciano acelerador. Leve o slider da esquerda para a direita e a malha inteira acende. Não é analogia — é a contagem de dispositivos endereçáveis.
Preciso ser direto sobre o que este desenho NÃO prova. OpenCL endereçar mais hardware é uma afirmação sobre a API, não sobre o desempenho. Em GPU NVIDIA, CUDA normalmente ganha — mais bibliotecas afinadas, mais controle de memória, mais gente otimizando. A minha tese não é "OpenCL é mais rápido". É "OpenCL não te tranca", e essas duas coisas são diferentes.
A imagem custa um hash por célula. A afirmação custa uma linha: toda GPU NVIDIA fala OpenCL; nem toda GPU que fala OpenCL fala CUDA.
shaders-gpu/alcance-runtime.glsl.
Warp em lockstep — e o custo da divergência
32 colunas = 32 lanes de um warp. A divergência acende em vermelho e o passo do warp é refeito serialmente para cada caminho.
Trinta e duas colunas: um warp. Cada linha dentro da coluna é uma instrução, e todas as 32 colunas andam juntas. Isso é SIMT. Aumente a divergência: algumas colunas saem do passo e o warp inteiro espera por elas. O vermelho é o custo — lanes ociosas.
Este é o argumento a favor de CUDA, e eu não vou escondê-lo. O modelo SIMT com garantia explícita de lockstep por warp permitiu à NVIDIA construir primitivas que OpenCL não tinha: shuffle, ballot, reduções cooperativas. OpenCL 2.0 trouxe subgroup operations justamente para cobrir essa lacuna — e a cobertura ainda é irregular entre drivers. Aqui CUDA ganha. O que estou argumentando não é que CUDA é pior; é que o ganho dele é técnico, e o preço dele é vendor lock-in.
Uma máscara de coluna e um passo de divergência. A conta real é a de lanes ociosas: um warp de 32 com k caminhos divergentes executa em até k passos.
shaders-gpu/warp-lockstep.glsl.
O mesmo kernel em GLSL, OpenCL e CUDA
O campo abaixo é gerado por uma única expressão de rotação de fase. Ela compila sem alteração nas três linguagens; o shader é literalmente essa linha.
Um campo vetorial. Cada seta é o resultado da mesma função, aplicada a uma coordenada diferente. Esse texto — a função inteira — é válido em GLSL, em OpenCL C e em CUDA, mudando só o nome do tipo. O desenho roda nas três linguagens porque não há nada nele além dessa expressão. Onde CUDA exigiria reescrita, aqui não há o que reescrever: a seta não sabe em que linguagem foi compilada.
Preciso separar duas coisas que costumam ser vendidas juntas. "Portável" aqui significa portável para o kernel que eu escrevo. Não significa que meu projeto inteiro compile nos três: o host diverge, o gerenciamento de memória diverge, e qualquer chamada a cuBLAS ou cuDNN me prende de volta. A portabilidade que eu reivindico é a do núcleo matemático — que, na minha pesquisa, é quase tudo o que importa.
Isto é uma expressão, não uma biblioteca. Se o seu código depende de cuBLAS, cuDNN, NCCL ou Thrust, a portabilidade acaba antes de começar.
shaders-gpu/portabilidade-kernel.glsl.
O que o meu M22 mediu — e o que ficou de fora
Quatro classes de hardware. Verde = medida de fato pelo meu M22. Cinza = existe no mundo e não entrou na minha amostra.
Quatro barras. Verde é o que eu realmente medi. A barra cinza-clara é o tamanho inteiro da amostra; a verde é a fração que meu código conseguiu tocar. Olhe a diferença. Ela não é pequena.
Esta é a parte que eu preciso dizer contra mim mesmo. No módulo M22 do meu benchmark a detecção de GPU é literalmente `torch.cuda.is_available()`. "GPU", no meu relatório, quer dizer "GPU NVIDIA". Rodo em CPU e em MPS. Não existe uma linha de OpenCL no meu código, e a coluna "GPU" das minhas tabelas nunca incluiu AMD, Intel, nem FPGA. Então eu não posso citar o meu próprio benchmark como evidência de que OpenCL é viável — eu não medi. O argumento desta página é sobre a API e o ecossistema, e ele está em aberto na parte que mais importa: desempenho.
Consertar isso não é caro: um backend OpenCL no M22, rodando o mesmo BenchmarkNetwork, com o mesmo protocolo de warmup e n_runs. É a próxima coisa que eu devo fazer, e não está feita.
shaders-gpu/divida-m22.glsl.
A camada de tradução: CUDA × OpenCL
A coluna verde é NVIDIA. As outras seis são AMD, Intel, Apple, FPGA e CPU. O slider alterna a camada do meio e mostra quem recebe luz.
Sete colunas, sete dispositivos. A luz desce e atravessa a camada de tradução. Sobre CUDA, ela bate numa parede: só a coluna NVIDIA recebe luz. Sobre OpenCL, ela passa em todas. Nada no desenho mudou entre os dois lados — só a camada do meio.
O que OpenCL não te dá de graça: você escreve a inicialização do dispositivo, escolhe plataforma, compila o kernel em runtime, e paga com um pequeno overhead de dispatch. E cada driver é um driver — diferenças de comportamento entre fabricantes existem e doem. Não estou vendendo OpenCL como gratuito. Estou dizendo que o custo dele é trabalho, e o custo do CUDA é dependência. São custos de naturezas diferentes, e eu prefiro o que eu posso pagar com o meu próprio esforço.
Inicialização de plataforma, contexto e fila de comando: ~150 linhas de host code que o CUDA resolve com três chamadas. É o preço, e ele é real.
shaders-gpu/camada-de-traducao.glsl.
O que eu não estou dizendo
Não estou dizendo que CUDA é ruim. O modelo SIMT dele é melhor documentado, as
bibliotecas (cuBLAS, cuDNN, NCCL, CUTLASS) não têm equivalente maduro em OpenCL,
e a experiência de depuração é superior — compute-sanitizer e
nsight não têm par. Se o seu problema é treinar um transformer em
NVIDIA hoje, CUDA é a resposta correta e OpenCL vai te custar semanas.
O que eu estou dizendo é mais estreito: o meu kernel não precisa disso. O que eu escrevo é aritmética sobre buffers — filtro de fase, rotação de quaternion, projeção de manifold. Nada nessa lista pede shuffle, nem tensor core, nem biblioteca afinada. Quando o problema não pede, a dependência só cobra.
Onde eu ainda estou errado
Eu não tenho números. Não rodei o M22 em OpenCL, não comparei o mesmo
BenchmarkNetwork entre clCreateProgramWithSource e
nvcc, e portanto não posso afirmar nada sobre overhead de dispatch,
qualidade dos drivers das três fabricantes, ou diferença de throughput num
kernel real. Isso é uma lacuna no meu trabalho, não uma vitória retórica.
A correção é mecânica e está na lista: um backend OpenCL no M22, mesmo protocolo,
mesmas seeds, mesmo n_runs.
Reproduzir localmente
Cada arquivo está em /shaders-gpu/ com extensão
.glsl e compila em qualquer ambiente WebGL2 — incluindo
twigl.app e
glsl.app, no modo
300 es. Nesses ambientes troque uRes por
resolution, uTime por time e
uParam1 por uma constante.