39 puntos por GN⁺ 2025-04-21 | 1 comentarios | Compartir por WhatsApp
  • Proyecto open source para aprender técnicas de programación de alto rendimiento en C/C++ y ensamblador con ejemplos prácticos
  • Incluye ejemplos de uso de bibliotecas optimizadas y varias técnicas de optimización de hardware en lugar de STL
  • Explica varios trucos de rendimiento como costo de generación de entrada, aproximación de funciones matemáticas, predicción de saltos del CPU y paralelización multinúcleo
  • Cubre ampliamente técnicas de optimización por plataforma y métodos para medir benchmarks, incluyendo CUDA, PTX, ASM, FPGA y procesamiento de JSON
  • Ofrece funciones para automatizar la ejecución de benchmarks y el procesamiento estadístico basadas en Google Benchmark

Cómo escribir código C/C++ y ensamblador orientado al rendimiento

  • Este proyecto es una colección de código de benchmark para ayudar a formar la intuición y la forma de pensar necesarias para diseñar software de alto rendimiento
  • Presenta ejemplos prácticos para evitar bugs, problemas de seguridad y cuellos de botella de rendimiento comunes en el código moderno
  • Introduce de forma sistemática técnicas reales orientadas al rendimiento que es difícil ver en clases universitarias o bootcamps
  • La mayor parte del código funciona en entornos Linux basados en GCC y Clang, aunque también hay soporte parcial para Windows y macOS
  • También presenta algoritmos paralelos, corrutinas y polimorfismo para implementar código de alto rendimiento

Temas principales

  • ¿Entrada aleatoria nada menos que 100 veces más barata? La generación de entrada puede ser más lenta que el algoritmo
  • 1% de error por 1/40 del costo: probar a aproximar funciones trigonométricas de STL como std::sin en solo 3 líneas de código
  • ¿La lógica perezosa es 4 veces más rápida? Llevar la evaluación diferida al extremo con std::ranges personalizado e iteradores
  • Optimización del compilador más allá de -O3: con flags ocultos y trucos se puede exprimir hasta 2 veces más rendimiento
  • ¿El problema es la multiplicación de matrices? Aunque hay 60% menos operaciones, un GEMM 3x3x3 puede ser 70% más lento que uno 4x4x4
  • ¿La verdad sobre el escalado de la IA? Medir la brecha entre el rendimiento teórico de ALU y el rendimiento real de BLAS
  • ¿Cuántos if ya son demasiados? Probar los límites del predictor de saltos del CPU con solo 10 líneas de código
  • ¿La recursión es mejor? Midamos directamente la profundidad de pila donde aparece un SEGFAULT
  • ¿Por qué conviene evitar excepciones? ¿Qué tal usar alternativas como std::error_code o std::variant?
  • ¿Quieres escalar a multinúcleo? Cómo usar OpenMP, Intel oneTBB o un thread pool hecho a mano
  • ¿Cómo procesar JSON sin asignación de memoria? ¿Será mejor C++20 o una herramienta antigua de C99 más simple?
  • Para usar bien los contenedores asociativos de STL, cómo aprovechar claves personalizadas y comparadores transparentes
  • ¿Y si hubiera una forma más rápida que un parser casero? Ir de frente con un motor de expresiones regulares basado en consteval
  • ¿El tamaño de los punteros realmente es de 64 bits? Pongamos a trabajar el pointer tagging
  • ¿Cuántos paquetes puede perder UDP? Hasta procesar solicitudes web desde espacio de usuario con io_uring
  • Implementación de operaciones vectorizadas sobre memoria no contigua 50% más rápidas con Scatter-Gather
  • ¿Intel oneAPI vs Nvidia CCCL? ¿Qué tienen de especial <thrust> y <cub>?
  • CUDA C++, PTX, SASS: ¿en qué se diferencian del código para CPU?
  • ¿Código sensible al rendimiento? Comparación de cuándo elegir intrinsics, asm inline o archivos .S
  • Tensor Core y estructura de memoria — ¿en qué se diferencian CPU y las GPU Volta, Ampere, Hopper y Blackwell?
  • ¿En qué se diferencia programar FPGA de GPU? ¿Cuáles son las diferencias entre síntesis de alto nivel (HLS), Verilog y VHDL? 🔜 #36
  • ¿Qué es un Encrypted Enclave? Comparación de latencia entre Intel SGX, AMD SEV y ARM Realm 🔜 #31

Cómo ejecutarlo y configurar el entorno

  • Se recomienda Linux + GCC; también se puede usar WSL o Clang en Mac (distribución no predeterminada)
  • Es necesario instalar CMake, liburing, OpenBLAS, g++ y build-essential
  • Si compilas y ejecutas el binario less_slow, los benchmarks se correrán automáticamente
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  
  • Se puede elegir si usar CUDA e Intel TBB (con flags como -D USE_INTEL_TBB=OFF)
  • Al ejecutar, puedes seleccionar solo ciertos benchmarks, guardar en JSON o definir el formato de salida
build_release/less_slow --benchmark_filter=std_sort  
build_release/less_slow --benchmark_out=results.json --benchmark_format=json  

Consejos para mejorar la medición de rendimiento

  • Desactivar SMT y usar random interleaving para minimizar el ruido
  • Con la opción --benchmark_perf_counters de Google Benchmark se pueden medir contadores de rendimiento de hardware
sudo build_release/less_slow --benchmark_perf_counters="CYCLES,INSTRUCTIONS"  
  • O también se puede medir el benchmark usando la herramienta perf de Linux
sudo perf stat taskset 0xEFFFEFFFEFFFEFFFEFFFEFFFEFFFEFFF build_release/less_slow --benchmark_filter=super_sort  

Estructura de archivos del proyecto

  • Fuente principal: less_slow.cpp (centrado en código de benchmark para CPU)
  • Incluye archivos de optimización por plataforma: ensamblador para x86/ARM, código CUDA .cu y PTX .ptx
├── less_slow.cpp           # Código principal de benchmark  
├── less_slow_amd64.S       # Ensamblador x86  
├── less_slow_aarch64.S     # Ensamblador 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: medición de rendimiento
  • Intel oneTBB: backend paralelo para STL
  • Meta libunifex: modelo de ejecución asíncrona
  • range-v3: reemplazo de std::ranges
  • fmt: reemplazo de std::format
  • StringZilla: reemplazo de std::string
  • CTRE: reemplazo de std::regex
  • nlohmann/json, yyjson: parsers de JSON
  • Abseil: contenedores de alto rendimiento
  • cppcoro: implementación de corrutinas
  • liburing: I/O de bypass del kernel de Linux
  • ASIO: networking asíncrono
  • Nvidia CCCL, CUTLASS: algoritmos de GPU y operaciones matriciales

Resumen de consejos para usar Google Benchmark

  • Registrar benchmarks con BENCHMARK() y pasar parámetros con ->Args({x,y})
  • Controlar la optimización del compilador con DoNotOptimize() y ClobberMemory()
  • Controlar número de iteraciones y tiempo del benchmark con ->Iterations(n) y ->MinTime(n)
  • Especificar complejidad temporal con ->Complexity(...) y ->SetComplexityN(n)
  • Controlar manualmente la sección cronometrada con state.PauseTiming() y ResumeTiming()
  • Se pueden registrar contadores personalizados con state.counters[...]

Memes y humor

  • Inserta imágenes de memes técnicos en el material educativo para hacerlo más atractivo
  • Expresa de forma humorística la oposición entre rendimiento y abstracción, así como el punto flotante IEEE 754

1 comentarios

 
GN⁺ 2025-04-21
Opiniones en Hacker News
  • Trigonometría 40 veces más rápida: se pueden acelerar funciones de biblioteca estándar como std::sin con 3 líneas de código

    • Se puede aproximar sin(x) limitando la expansión a unos pocos términos
    • El costo computacional baja, pero la precisión también
    • La pérdida de precisión está subestimada. Es muy impreciso para entradas fuera del rango [-2, 2]
    • Ni siquiera puede manejar un solo período de la onda seno y tampoco trata su naturaleza periódica. Es una "optimización" inútil
  • Experiencia compartida en microcontroladores

    • Trabaja en sistemas embebidos, donde el heap es de unos 256 KiB y la pila más grande es de 4 KiB
    • Usa principalmente C++ moderno, pero no todos los trucos sirven para todas las situaciones
    • CTRE está bien siempre que se evite el desbordamiento de pila. Intentó validar cadenas de configuración de proxy HTTP, pero el sistema se cayó por un stack overflow
    • Casi no usa JSON internamente y escribió su propia biblioteca BSON. Así no tiene que preocuparse por la asignación de memoria ni por la fragmentación
    • Usa picolibc en lugar de newlib y eliminó el código de locale de la biblioteca estándar de C/C++. Esto reduce el tamaño del programa
  • Opinión sobre la elección de Abseil

    • Cuando apareció por primera vez fue un gran tema, pero ahora existen varias alternativas que mejoraron sus debilidades
    • En los últimos años han aumentado las quejas sobre Abseil. Hubo salidas de mantenedores de bibliotecas clave en Google
  • Crítica a las distorsiones en C++ por rendimiento

    • Le sorprende que CTRE dé buenos resultados. Quiere investigarlo más a fondo
    • Quiere revisar benchmarks de thread pools de OpenMP y TBB, y ver si se puede agregar el thread pool de Boost::ASIO
  • Diferencias entre programar para FPGA y GPU, y solicitud de contenido sobre síntesis avanzada, Verilog y VHDL

    • Le gustaría ver más solicitudes priorizadas sobre este tema
  • Información nueva sobre números de punto flotante desnormalizados

    • A veces se lo pregunta al multiplicar matrices en GPU
  • Retroalimentación positiva sobre la publicación de Google Benchmark

    • Está bien el enfoque en benchmarking de rendimiento. El repositorio está bien organizado
  • Expectativas sobre "código menos lento en C, C++ y ensamblador"

    • Esperaba que también incluyera código C, pero solo están .cpp y .S
    • less_slow.cpp usa muchas características de C++. Habría que quitar la "C" de la lista o corregirlo