Benchmarking 20 programming languages on N-queens and matrix multiplication

Benchmarks comparando 20 linguagens de programação em N-queens e multiplicação de matrizes estão provocando debate sobre o que esses testes realmente medem: velocidade bruta da linguagem/runtime, qualidade do compilador ou a força de ecossistemas como NumPy e BLAS. Comentadores destacam como implementações ingênuas e sem bibliotecas podem fazer Python e outras linguagens dinâmicas parecerem desproporcionalmente lentas em comparação com C, Julia ou Nim, e questionam se o warmup do JIT, o layout de memória e o código não idiomático distorcem os resultados. Muitos argumentam que, embora esses microbenchmarks sejam úteis para entender características de desempenho em baixo nível, eles dizem pouco sobre fluxos de trabalho típicos do mundo real, nos quais bibliotecas otimizadas, solvers e ferramentas específicas de domínio predominam.

Objetivos e metodologia dos benchmarks

  • Os benchmarks cobrem N-queens, multiplicação de matrizes, sudoku e bed coverage, com implementações em sua maioria feitas à mão e “ingênuas”, muitas vezes inspiradas em exemplos no estilo Rosetta.
  • Alguns veem isso como útil para avaliar o desempenho basal de uma linguagem/VM quando é preciso implementar um novo algoritmo por conta própria.
  • Outros argumentam que o código é subótimo e não idiomático em várias भाषagens, então os resultados podem dizer mais sobre implementações específicas do que sobre as linguagens.
  • Há debate sobre incluir warmup do JIT e tempo de inicialização; alguns dizem que “do CLI ao resultado” é realista, outros querem JITs aquecidos ou também contar o tempo de compilação para linguagens AOT.

Bibliotecas vs desempenho “da linguagem pura”

  • Um grande tópico contesta excluir bibliotecas como NumPy, BLAS ou bibliotecas especializadas de tensor.
  • Um lado: benchmarks devem comparar linguagens sem bibliotecas C/Fortran externas; caso contrário, você só mede o overhead de FFI.
  • O outro lado: Python numérico idiomático, C#, etc. sempre usam essas bibliotecas; matmul em linguagem pura é irrealista e distorce o desempenho do mundo real.
  • Observa-se que praticamente toda multiplicação de matrizes de alto desempenho em qualquer linguagem acaba dependendo de bibliotecas altamente otimizadas, muitas vezes com assembly, e que Python normalmente é “rápido” apenas quando chama essas bibliotecas.

Dificuldade da multiplicação de matrizes e BLAS

  • Vários comentários enfatizam que uma matmul realmente no nível do BLAS exige tiling cuidadoso, SIMD, kernels conscientes de cache/registradores e às vezes assembly; compiladores não alcançarão >90% do pico com um simples triple loop.
  • Alguns afirmam que é possível igualar ou superar o BLAS para formas/padrões de esparsidade específicos com controle no nível de C++/Nim; outros rebatem, relatando experimentos em que código escrito à mão ficou muito atrás do OpenBLAS, exceto com esforço significativo.
  • Um projeto em Nim é citado com benchmarks mostrando desempenho comparável ao OpenBLAS e ao MKL em várias CPUs, usando microkernels SIMD gerados e threading personalizado.

Observações específicas por linguagem

  • Rust: a matmul inicial era lenta devido ao layout Vec<Vec> e às verificações de limites; implementações baseadas em iteradores ou com tamanhos estáticos agora se igualam ao C.
  • C#: a matmul inicial usava arrays retangulares (multiplicações extras por acesso). Trocar para array de arrays aproxima o desempenho do Java; ganhos adicionais são esperados com SIMD (Vector<T>/System.Numerics.Tensors).
  • Swift: a matmul pode igualar C/Rust após otimização, mas o sudoku continua lento devido a muitas alocações na heap e à falta de arrays estáticos.
  • Julia: a matmul inicial usava a orientação de memória errada e tinha SIMD desativado; o código corrigido agora fica próximo do C.
  • Mojo: cerca de 2× mais lento que C na matmul, visto por alguns como aceitável dada a ergonomia, embora outros o achem verboso em comparação com Julia.

JIT, inicialização e hardware

  • Alguns argumentam que linguagens com JIT são prejudicadas por execuções a frio; outros respondem que muitos usos reais (CLIs, builds) são de curta duração, então o desempenho a frio importa.
  • Sistemas ARM big.LITTLE levantam dúvidas sobre se os benchmarks rodam em núcleos de performance ou de eficiência.
  • Detalhes mono, multi-thread e de vetorização (por exemplo, threading do OpenBLAS, uso de AVX/AVX-512, verificações de limites) afetam significativamente as diferenças observadas.

Gráficos e métricas alternativas

  • Gráficos de barras empilhadas obscurecem comparações quando algumas linguagens (PHP, Ruby, Perl, pure CPython) são extremamente lentas; sugerem-se gráficos separados, escalas logarítmicas ou ops/sec.
  • Alguns querem métricas adicionais como tamanho do código comprimido em gzip ou LOC para refletir expressividade e complexidade do código; outros observam a dificuldade de definir uma métrica de tamanho justa e centrada no humano.
  • Vários comentaristas apontam para suítes de benchmark maiores (por exemplo, o Computer Language Benchmarks Game) e pedem mais tarefas do mundo real (I/O de arquivos, parsing de JSON, servidores).