05_22_09_parallel_calculator - El Motor de Cálculo Masivo¶
PROPÓSITO¶
Sistema de procesamiento paralelo para cálculo masivo de coeficientes. Usa SIMD (SSE/AVX/AVX2/AVX-512) para procesar 4-16 sets de coeficientes simultáneamente en CPU, y GPU (CUDA/Metal/OpenCL) para miles de instancias en paralelo. Esencial para síntesis masivamente polifónica y modulación compleja.
Responsabilidad: Acelerar cálculo de coeficientes mediante paralelización, logrando speedups de 4x-16x (SIMD) y 100x+ (GPU).
ESTRUCTURA¶
05_22_09_parallel_calculator/
├── include/
│ ├── parallel_calculator.hpp # Calculador paralelo principal
│ ├── simd_intrinsics.hpp # Wrappers de intrinsics
│ └── gpu_kernels.hpp # Headers de GPU kernels
├── src/
│ ├── simd_calculator.cpp # Implementación SIMD (AVX)
│ ├── gpu_calculator.cpp # Implementación GPU
│ └── thread_pool.cpp # Thread pool para CPU
├── tests/
│ ├── test_simd_correctness.cpp # Verificación SIMD vs scalar
│ ├── test_gpu_accuracy.cpp # Verificación GPU
│ └── benchmark_parallel.cpp # Benchmarks de speedup
├── examples/
│ ├── massive_polyphony.cpp # 1024 voces simultáneas
│ └── realtime_modulation.cpp # Modulación de parámetros
└── README.md
API PRINCIPAL¶
ParallelCalculator¶
class ParallelCalculator {
public:
enum class Method {
SCALAR, // Single threaded, no SIMD
SIMD, // SSE/AVX/AVX2/AVX512
GPU, // CUDA/Metal/OpenCL
AUTO // Detect best method
};
// Batch calculation
void calculate_batch(
const FilterParams* params, // Array de parámetros
BiquadCoeffs* results, // Array de resultados
size_t count, // Número de elementos
Method method = Method::AUTO
);
// SIMD específico (procesa 4/8/16 simultáneamente)
void calculate_batch_simd(
const FilterParams* params,
BiquadCoeffs* coeffs,
size_t count
);
// GPU específico (procesa miles simultáneamente)
void calculate_batch_gpu(
DeviceParams* d_params,
DeviceCoeffs* d_coeffs,
size_t count
);
// Configuration
void set_parallelism(Method method);
Method get_optimal_method(size_t count);
};
SIMD: VECTOR PROCESSING EN CPU¶
Conceptos¶
SIMD = Single Instruction Multiple Data Procesa múltiples datos con una instrucción:
Scalar (1 cálculo):
freq1 → calculate() → coeffs1
SIMD AVX (8 cálculos simultáneos):
[freq1, freq2, ..., freq8] → calculate_simd() → [coeffs1, ..., coeffs8]
Instruction Sets¶
| Instruction Set | Datos Simultáneos | Año | Processors |
|---|---|---|---|
| SSE | 4 floats | 1999 | Pentium III+ |
| AVX | 8 floats | 2011 | Sandy Bridge+ |
| AVX2 | 8 floats + FMA | 2013 | Haswell+ |
| AVX-512 | 16 floats | 2016 | Skylake-X+ |
Ejemplo AVX¶
void calculate_batch_simd(const float* freq, BiquadCoeffs* coeffs, size_t count) {
// Process 8 frequencies at once
for (size_t i = 0; i < count; i += 8) {
// Load 8 frequencies into AVX register
__m256 freqs = _mm256_load_ps(&freq[i]);
// Calculate omega = 2π * freq / sr (for 8 freqs at once)
__m256 two_pi_over_sr = _mm256_set1_ps(2.0f * M_PI / 44100.0f);
__m256 omega = _mm256_mul_ps(freqs, two_pi_over_sr);
// Calculate cos(omega) for 8 values
__m256 cos_omega = _mm256_cos_ps(omega);
// ... más cálculos vectorizados ...
// Store results for 8 coeffs
_mm256_store_ps(&coeffs[i].b0, b0);
// etc.
}
}
GPU: MASSIVE PARALLELISM¶
Conceptos¶
GPUs tienen miles de cores (ej: RTX 3080 = 8704 CUDA cores) Ideal para cálculo de coeficientes de miles de voces simultáneas.
CUDA Kernel Example¶
__global__ void calculate_filter_coeffs_kernel(
const FilterParams* params,
BiquadCoeffs* coeffs,
int count
) {
int idx = blockIdx.x * blockDim.x + threadIdx.x;
if (idx >= count) return;
// Cada thread calcula coeficientes para una voz
float freq = params[idx].frequency;
float q = params[idx].q;
// Cálculos (ejecutados en paralelo en miles de threads)
float omega = 2.0f * M_PI * freq / 44100.0f;
float cos_omega = cosf(omega);
float sin_omega = sinf(omega);
float alpha = sin_omega / (2.0f * q);
// Coeficientes Butterworth LPF
coeffs[idx].b0 = (1.0f - cos_omega) / 2.0f;
coeffs[idx].b1 = 1.0f - cos_omega;
coeffs[idx].b2 = (1.0f - cos_omega) / 2.0f;
coeffs[idx].a1 = -2.0f * cos_omega;
coeffs[idx].a2 = 1.0f - alpha;
}
PERFORMANCE COMPARISON¶
Benchmark: Calcular 1024 sets de coeficientes Butterworth LPF¶
| Method | Time (ms) | Speedup |
|---|---|---|
| Scalar | 10.24 | 1.0x |
| SSE (4) | 2.56 | 4.0x |
| AVX (8) | 1.28 | 8.0x |
| AVX-512(16) | 0.64 | 16.0x |
| GPU (CUDA) | 0.10 | 102.4x |
Nota: GPU tiene overhead de transferencia de datos (CPU→GPU→CPU), solo vale la pena para >1000 coeficientes.
AUTOMATIC METHOD SELECTION¶
Method ParallelCalculator::get_optimal_method(size_t count) {
if (count < 100) {
return Method::SCALAR; // Overhead no vale la pena
}
else if (count < 10000) {
if (has_avx512()) return Method::SIMD;
if (has_avx2()) return Method::SIMD;
if (has_avx()) return Method::SIMD;
return Method::SCALAR;
}
else {
if (has_gpu()) return Method::GPU;
return Method::SIMD; // Fallback
}
}
RUNTIME CPU FEATURE DETECTION¶
struct CPUFeatures {
bool has_sse;
bool has_avx;
bool has_avx2;
bool has_avx512;
};
CPUFeatures detect_cpu_features() {
CPUFeatures features;
#ifdef _MSC_VER
int cpuInfo[4];
__cpuid(cpuInfo, 1);
features.has_sse = (cpuInfo[3] & (1 << 25)) != 0;
features.has_avx = (cpuInfo[2] & (1 << 28)) != 0;
// ... más checks
#else
__builtin_cpu_init();
features.has_sse = __builtin_cpu_supports("sse");
features.has_avx = __builtin_cpu_supports("avx");
// ... más checks
#endif
return features;
}
MEMORY ALIGNMENT¶
SIMD requiere memoria alineada (16/32/64 bytes):
// ❌ MAL - memoria no alineada
float* data = new float[1024];
// ✅ BIEN - memoria alineada 64 bytes (AVX-512)
float* data = (float*)_aligned_malloc(1024 * sizeof(float), 64);
// O usar allocator custom
alignas(64) float data[1024];
EJEMPLO DE USO¶
#include "parallel_calculator.hpp"
// Preparar datos de entrada (1024 filtros)
FilterParams params[1024];
for (int i = 0; i < 1024; i++) {
params[i].frequency = 100.0f + i * 10.0f; // 100Hz a 10kHz
params[i].q = 0.707f;
}
// Reservar memoria para resultados (alineada)
BiquadCoeffs* coeffs = (BiquadCoeffs*)_aligned_malloc(
1024 * sizeof(BiquadCoeffs), 64
);
// Calcular en paralelo (automático selecciona mejor método)
ParallelCalculator calc;
calc.calculate_batch(params, coeffs, 1024, Method::AUTO);
// Usar coeficientes...
for (int i = 0; i < 1024; i++) {
std::cout << "Voice " << i << ": b0=" << coeffs[i].b0 << "\n";
}
_aligned_free(coeffs);
CASO DE USO: SÍNTESIS POLIFÓNICA¶
// Sintetizador con 128 voces
const int NUM_VOICES = 128;
FilterParams voice_params[NUM_VOICES];
void update_filter_modulation() {
// Cada voz tiene filtro con frecuencia modulada por LFO
for (int i = 0; i < NUM_VOICES; i++) {
float lfo = sin(lfo_phase[i]);
voice_params[i].frequency = base_freq[i] + lfo * mod_amount;
}
// Recalcular coeficientes para todas las voces (SIMD)
calc.calculate_batch_simd(voice_params, voice_coeffs, NUM_VOICES);
// Proceso de audio usa los coeficientes actualizados
}
TAREAS DE DESARROLLO¶
- Implementar ParallelCalculator class
- Implementar SIMD variants (SSE, AVX, AVX2, AVX-512)
- Implementar GPU kernels (CUDA, Metal, OpenCL)
- Implementar CPU feature detection en runtime
- Implementar automatic method selection
- Implementar memory alignment utilities
- Thread pool para cálculos no-SIMD
- Tests de correctitud (SIMD vs scalar)
- Benchmarks de speedup en diferentes CPUs/GPUs
- Documentación de optimization techniques
MÉTRICAS DE CALIDAD¶
✅ SIMD correctness: 100% match con scalar ✅ AVX speedup: 8x vs scalar ✅ AVX-512 speedup: 16x vs scalar ✅ GPU speedup: >100x para >10k coeficientes ✅ Automatic method selection funciona correctamente ✅ Memory alignment verificado (0 crashes)
REFERENCIAS¶
- SIMD Programming: Intel Intrinsics Guide
- GPU Computing: CUDA C Programming Guide
- Optimization: "Software Optimization Cookbook" - Intel
Este módulo es opcional pero crucial para performance en síntesis compleja y modulación masiva.