- Projeto open source para aprender técnicas de código C/C++ e assembly de alto desempenho com exemplos práticos
- Inclui exemplos de uso de bibliotecas otimizadas no lugar da STL e várias técnicas de otimização de hardware
- Explica diversos truques de performance, como custo de geração de entrada, aproximação de funções matemáticas, predição de desvios da CPU e paralelização multicore
- Cobre amplamente técnicas de otimização por plataforma e até métodos de medição de benchmark, incluindo CUDA, PTX, ASM, FPGA e processamento de JSON
- Oferece recursos de automação da execução de benchmarks e do processamento estatístico com base no Google Benchmark
Como escrever código C/C++ e assembly orientado a desempenho
- Este projeto é uma coleção de códigos de benchmark para ajudar a desenvolver a intuição e a forma de pensar necessárias ao design de software de alto desempenho
- Traz exemplos práticos de código para evitar bugs, problemas de segurança e gargalos de desempenho comuns no código moderno
- Apresenta de forma sistemática técnicas práticas orientadas a performance que são difíceis de encontrar em aulas universitárias ou bootcamps
- A maior parte do código roda em ambientes Linux baseados em GCC e Clang, com suporte parcial a Windows e macOS
- Também apresenta algoritmos paralelos, corrotinas e polimorfismo para implementação de código de alto desempenho
Principais tópicos
- Entradas aleatórias 100x mais baratas?! O fato de que gerar a entrada pode ser mais lento do que o próprio algoritmo
- Erro de 1% por 1/40 do custo: aproximando funções trigonométricas da STL como
std::sin em apenas 3 linhas de código
- Lógica preguiçosa 4x mais rápida? Implementando preguiça extrema com
std::ranges customizado e iteradores
- Otimizações de compilador além de
-O3: flags escondidas e truques podem extrair o dobro de performance
- O problema é multiplicação de matrizes? Mesmo com 60% menos operações, um GEMM 3x3x3 pode ser 70% mais lento que 4x4x4
- A verdade sobre o escalonamento de IA? Medindo a diferença entre o throughput teórico da ALU e a performance real de BLAS
- Quantos
ifs são demais? Testando os limites do preditor de desvios da CPU com apenas 10 linhas de código
- Recursão é melhor? Vamos medir diretamente a profundidade da pilha para ver onde ocorre
SEGFAULT
- Por que evitar exceções? Que tal usar alternativas como
std::error_code ou std::variant?
- Quer escalar em multicore? Como usar OpenMP, Intel oneTBB ou um thread pool feito à mão
- Como processar JSON sem alocação de memória? C++20 é melhor ou ferramentas antigas em C99 são mais simples?
- Para usar bem os contêineres associativos da STL, como aproveitar chaves customizadas e comparadores transparentes?
- E se houver algo mais rápido que um parser artesanal? Um duelo direto com um motor de regex baseado em
consteval
- O tamanho de um ponteiro é mesmo 64 bits? Vamos usar pointer tagging
- Quanto o UDP realmente perde em pacotes? Indo até o processamento de requisições web em espaço de usuário com
io_uring
- Implementação de operações vetorizadas em memória não contígua 50% mais rápidas com Scatter-Gather
- Intel oneAPI vs Nvidia CCCL? O que há de especial em
<thrust> e <cub>?
- CUDA C++, PTX, SASS: em que diferem do código de CPU?
- Para código sensível a desempenho, comparação entre intrinsics,
asm inline e arquivos .S para escolher a melhor opção
- Tensor Core e estrutura de memória — como diferem CPU e GPUs Volta, Ampere, Hopper e Blackwell?
- Como a programação para FPGA difere de GPU? Quais são as diferenças entre síntese de alto nível (HLS), Verilog e VHDL? 🔜 #36
- O que é um Encrypted Enclave? Comparação de latência entre Intel SGX, AMD SEV e ARM Realm 🔜 #31
Como executar e configurar o ambiente
- Recomendado: Linux + GCC; também é possível usar WSL ou Clang no Mac (distribuição não padrão)
- É preciso instalar
CMake, liburing, OpenBLAS, g++ e build-essential
- Depois de compilar o executável
less_slow, a execução dispara automaticamente os benchmarks
git clone https://github.com/ashvardanian/less_slow.cpp.git
cd less_slow.cpp
pip install cmake --upgrade
sudo apt install -y build-essential g++ liburing-dev libopenblas-base
cmake -B build_release -D CMAKE_BUILD_TYPE=Release
cmake --build build_release --config Release
build_release/less_slow
- É possível escolher se vai usar CUDA e Intel TBB (com flags como
-D USE_INTEL_TBB=OFF)
- Na execução, dá para selecionar apenas benchmarks específicos, salvar em JSON ou definir o formato de saída
build_release/less_slow --benchmark_filter=std_sort
build_release/less_slow --benchmark_out=results.json --benchmark_format=json
Dicas para melhorar a medição de desempenho
- Desabilite SMT e use random interleaving para minimizar ruído
- A opção
--benchmark_perf_counters do Google Benchmark permite medir contadores de desempenho de hardware
sudo build_release/less_slow --benchmark_perf_counters="CYCLES,INSTRUCTIONS"
- Ou use a ferramenta
perf do Linux para medir os benchmarks
sudo perf stat taskset 0xEFFFEFFFEFFFEFFFEFFFEFFFEFFFEFFF build_release/less_slow --benchmark_filter=super_sort
Estrutura de arquivos do projeto
- Fonte principal:
less_slow.cpp (foco no código de benchmark para CPU)
- Inclui arquivos de otimização por plataforma: assembly para x86/ARM, código CUDA
.cu e PTX .ptx
├── less_slow.cpp # código principal de benchmark
├── less_slow_amd64.S # assembly x86
├── less_slow_aarch64.S # assembly ARM
├── less_slow.cu # CUDA C++
├── less_slow_sm70.ptx # PTX IR (Volta)
├── less_slow_sm90a.ptx # PTX IR (Hopper)
Uso de bibliotecas externas
- Google Benchmark: medição de desempenho
- Intel oneTBB: backend paralelo para STL
- Meta libunifex: modelo de execução assíncrona
- range-v3: substituto para
std::ranges
- fmt: substituto para
std::format
- StringZilla: substituto para
std::string
- CTRE: substituto para
std::regex
- nlohmann/json, yyjson: parsers JSON
- Abseil: contêineres de alto desempenho
- cppcoro: implementação de corrotinas
- liburing: I/O em Linux sem passar pela camada tradicional do kernel
- ASIO: rede assíncrona
- Nvidia CCCL, CUTLASS: algoritmos de GPU e operações com matrizes
Resumo de dicas de uso do Google Benchmark
- Registre benchmarks com
BENCHMARK() e passe parâmetros com ->Args({x,y})
- Controle otimizações do compilador com
DoNotOptimize() e ClobberMemory()
- Controle número de repetições e tempo de benchmark com
->Iterations(n) e ->MinTime(n)
- Defina complexidade temporal com
->Complexity(...) e ->SetComplexityN(n)
- Controle manualmente o trecho cronometrado com
state.PauseTiming() e ResumeTiming()
- É possível registrar contadores customizados com
state.counters[...]
Memes e humor
- Inserção de imagens de memes técnicos no material educacional para despertar interesse
- Oposição entre performance e abstração, IEEE 754 de ponto flutuante etc. expressos com humor
1 comentários
Comentários no Hacker News
Trigonometria 40x mais rápida: dá para acelerar funções da biblioteca padrão como
std::sincom 3 linhas de códigosin(x)limitando a expansão a alguns termosCompartilhando experiência com microcontroladores
Opinião sobre as escolhas do Abseil
Crítica às distorções em nome do desempenho em C++
Diferenças entre programação para FPGA e GPU, e pedido por conteúdo sobre síntese de alto nível, Verilog e VHDL
Nova informação sobre números de ponto flutuante desnormalizados
Feedback positivo sobre o post do Google Benchmark
Expectativa sobre "código C, C++ e assembly menos lento"
.cppe.Sless_slow.cppusa muitos recursos de C++. Seria melhor remover o "C" da lista ou ajustar isso