Skip to content

Detección y optimización automática para hardware AMD #2

Description

@Charly-bite

Optimización específica para hardware AMD - Detección y configuración automática

Objetivo

Crear un sistema de detección automática de hardware AMD y optimizar la aplicación de transcripción específicamente para procesadores y GPUs AMD.

Contexto

  • Sistema confirmado: No compatible con Lightning Whisper MLX (solo Apple Silicon)
  • Hardware: Procesador y GPU AMD (modelos por determinar)
  • Enfoque: Optimización máxima para arquitectura AMD
  • Estrategia: whisper.cpp + optimizaciones AMD específicas

Scripts de Detección de Hardware

1. Script Principal de Detección AMD

#!/usr/bin/env python3
"""
Detección completa de hardware AMD y optimización automática
"""

import subprocess
import platform
import psutil
import re
import json
from pathlib import Path

class AMDHardwareDetector:
    def __init__(self):
        self.system_info = {}
        self.optimization_config = {}
    
    def detect_cpu(self):
        """Detectar CPU AMD específico"""
        print("🔍 Detectando CPU AMD...")
        
        try:
            # Información básica del CPU
            cpu_info = {
                "brand": platform.processor(),
                "physical_cores": psutil.cpu_count(logical=False),
                "logical_cores": psutil.cpu_count(logical=True),
                "max_frequency": psutil.cpu_freq().max if psutil.cpu_freq() else "N/A"
            }
            
            # Detectar modelo específico AMD
            if platform.system() == "Linux":
                try:
                    # Leer /proc/cpuinfo para detalles específicos
                    with open('/proc/cpuinfo', 'r') as f:
                        cpuinfo = f.read()
                    
                    # Extraer información AMD específica
                    model_match = re.search(r'model name\s*:\s*(.+)', cpuinfo)
                    if model_match:
                        cpu_info["model_name"] = model_match.group(1).strip()
                    
                    # Detectar arquitectura AMD (Zen, Zen2, Zen3, Zen4)
                    model_name = cpu_info.get("model_name", "").lower()
                    if "ryzen" in model_name:
                        if "5000" in model_name or "6000" in model_name:
                            cpu_info["architecture"] = "Zen3"
                        elif "4000" in model_name:
                            cpu_info["architecture"] = "Zen2"
                        elif "3000" in model_name:
                            cpu_info["architecture"] = "Zen2"
                        elif "2000" in model_name:
                            cpu_info["architecture"] = "Zen+"
                        elif "1000" in model_name:
                            cpu_info["architecture"] = "Zen"
                        else:
                            cpu_info["architecture"] = "Unknown Zen"
                    
                    # Detectar caché L3
                    cache_match = re.search(r'cache size\s*:\s*(\d+)\s*KB', cpuinfo)
                    if cache_match:
                        cpu_info["cache_size_kb"] = int(cache_match.group(1))
                    
                except Exception as e:
                    print(f"⚠️ Error leyendo /proc/cpuinfo: {e}")
            
            # Usar lscpu si está disponible
            try:
                lscpu_output = subprocess.check_output(['lscpu'], text=True)
                
                # Extraer información adicional
                for line in lscpu_output.split('\n'):
                    if 'Model name:' in line:
                        cpu_info["lscpu_model"] = line.split(':', 1)[1].strip()
                    elif 'CPU family:' in line:
                        cpu_info["cpu_family"] = line.split(':', 1)[1].strip()
                    elif 'Stepping:' in line:
                        cpu_info["stepping"] = line.split(':', 1)[1].strip()
                    elif 'L3 cache:' in line:
                        cpu_info["l3_cache"] = line.split(':', 1)[1].strip()
                        
            except (subprocess.CalledProcessError, FileNotFoundError):
                print("⚠️ lscpu no disponible")
            
            self.system_info["cpu"] = cpu_info
            print(f"✅ CPU detectado: {cpu_info.get('model_name', 'AMD CPU')}")
            
        except Exception as e:
            print(f"❌ Error detectando CPU: {e}")
    
    def detect_gpu(self):
        """Detectar GPU AMD específica"""
        print("🔍 Detectando GPU AMD...")
        
        gpu_info = {}
        
        try:
            # Usar lspci para detectar GPU AMD
            lspci_output = subprocess.check_output(['lspci', '-v'], text=True)
            
            amd_gpus = []
            for line in lspci_output.split('\n'):
                if 'VGA compatible controller' in line and ('AMD' in line or 'ATI' in line):
                    amd_gpus.append(line.strip())
            
            if amd_gpus:
                gpu_info["detected_gpus"] = amd_gpus
                
                # Extraer modelo específico
                for gpu in amd_gpus:
                    if "RX" in gpu:
                        rx_match = re.search(r'RX\s*(\d+)', gpu)
                        if rx_match:
                            gpu_info["series"] = f"RX {rx_match.group(1)}"
                    elif "Vega" in gpu:
                        gpu_info["series"] = "Vega"
                    elif "RDNA" in gpu:
                        gpu_info["architecture"] = "RDNA"
                        
        except (subprocess.CalledProcessError, FileNotFoundError):
            print("⚠️ lspci no disponible")
        
        # Intentar con glxinfo si está disponible
        try:
            glxinfo_output = subprocess.check_output(['glxinfo', '-B'], text=True)
            
            for line in glxinfo_output.split('\n'):
                if 'OpenGL renderer string:' in line:
                    gpu_info["opengl_renderer"] = line.split(':', 1)[1].strip()
                elif 'OpenGL vendor string:' in line:
                    gpu_info["vendor"] = line.split(':', 1)[1].strip()
                    
        except (subprocess.CalledProcessError, FileNotFoundError):
            print("⚠️ glxinfo no disponible")
        
        # Verificar drivers AMD
        try:
            # Verificar módulos del kernel AMD
            lsmod_output = subprocess.check_output(['lsmod'], text=True)
            amd_modules = []
            
            for line in lsmod_output.split('\n'):
                if any(module in line for module in ['amdgpu', 'radeon', 'fglrx']):
                    amd_modules.append(line.split()[0])
            
            if amd_modules:
                gpu_info["loaded_drivers"] = amd_modules
                
        except (subprocess.CalledProcessError, FileNotFoundError):
            print("⚠️ lsmod no disponible")
        
        self.system_info["gpu"] = gpu_info
        if gpu_info:
            print(f"✅ GPU AMD detectada: {gpu_info.get('series', 'AMD GPU')}")
        else:
            print("⚠️ No se detectó GPU AMD específica")
    
    def detect_memory(self):
        """Detectar configuración de memoria"""
        print("🔍 Detectando memoria del sistema...")
        
        memory_info = {
            "total_gb": round(psutil.virtual_memory().total / (1024**3), 2),
            "available_gb": round(psutil.virtual_memory().available / (1024**3), 2),
            "used_percent": psutil.virtual_memory().percent
        }
        
        # Detectar velocidad de RAM si es posible
        try:
            dmidecode_output = subprocess.check_output(['sudo', 'dmidecode', '-t', 'memory'], text=True)
            
            speeds = re.findall(r'Speed:\s*(\d+)\s*MT/s', dmidecode_output)
            if speeds:
                memory_info["ram_speed_mhz"] = max([int(speed) for speed in speeds])
                
        except (subprocess.CalledProcessError, FileNotFoundError, PermissionError):
            print("⚠️ No se puede acceder a dmidecode (requiere sudo)")
        
        self.system_info["memory"] = memory_info
        print(f"✅ Memoria: {memory_info['total_gb']}GB total")
    
    def generate_amd_optimizations(self):
        """Generar configuraciones optimizadas para AMD"""
        print("⚙️ Generando optimizaciones AMD...")
        
        cpu_info = self.system_info.get("cpu", {})
        gpu_info = self.system_info.get("gpu", {})
        memory_info = self.system_info.get("memory", {})
        
        # Optimizaciones para whisper.cpp en AMD
        whisper_optimizations = {
            "threads": min(cpu_info.get("physical_cores", 4), 8),  # Óptimo para AMD
            "processors": 1,
            "use_gpu": len(gpu_info.get("loaded_drivers", [])) > 0,
            "model_size": "base",  # Empezar conservador
        }
        
        # Ajustar según arquitectura Zen
        architecture = cpu_info.get("architecture", "")
        if "Zen3" in architecture:
            whisper_optimizations.update({
                "threads": cpu_info.get("physical_cores", 8),
                "model_size": "small",  # Zen3 puede manejar modelos más grandes
                "enable_avx2": True,
                "batch_size": 1
            })
        elif "Zen2" in architecture:
            whisper_optimizations.update({
                "threads": max(4, cpu_info.get("physical_cores", 4) - 2),
                "enable_avx2": True,
                "batch_size": 1
            })
        
        # Ajustar según memoria disponible
        total_memory = memory_info.get("total_gb", 8)
        if total_memory >= 16:
            whisper_optimizations["model_size"] = "small"
            if total_memory >= 32:
                whisper_optimizations["model_size"] = "medium"
        
        # Optimizaciones específicas para GPU AMD
        if gpu_info.get("series"):
            gpu_series = gpu_info.get("series", "")
            if "RX" in gpu_series:
                rx_number = int(re.search(r'\d+', gpu_series).group()) if re.search(r'\d+', gpu_series) else 0
                if rx_number >= 6000:  # RX 6000 series o superior
                    whisper_optimizations.update({
                        "use_gpu": True,
                        "gpu_acceleration": "opencl",
                        "model_size": "small"
                    })
        
        self.optimization_config = {
            "whisper_cpp": whisper_optimizations,
            "system_tweaks": {
                "cpu_governor": "performance",
                "disable_cpu_mitigations": False,  # Mantener seguridad
                "huge_pages": total_memory >= 16
            }
        }
        
        print("✅ Optimizaciones generadas")
    
    def save_system_profile(self):
        """Guardar perfil del sistema para referencia"""
        profile_data = {
            "detection_date": "2025-06-14",
            "system_info": self.system_info,
            "optimizations": self.optimization_config
        }
        
        profile_path = Path("system_profile_amd.json")
        with open(profile_path, 'w') as f:
            json.dump(profile_data, f, indent=2)
        
        print(f"💾 Perfil guardado en: {profile_path}")
        return profile_path
    
    def generate_config_files(self):
        """Generar archivos de configuración optimizados"""
        
        # Configuración para whisper.cpp
        whisper_config = f"""# Configuración optimizada para AMD
whisper_cpp_amd:
  binary_path: "whisper"
  model_size: "{self.optimization_config['whisper_cpp']['model_size']}"
  threads: {self.optimization_config['whisper_cpp']['threads']}
  processors: {self.optimization_config['whisper_cpp']['processors']}
  use_gpu: {self.optimization_config['whisper_cpp']['use_gpu']}
  language_detection: true
  
  # Optimizaciones AMD específicas
  amd_optimizations:
    cpu_architecture: "{self.system_info['cpu'].get('architecture', 'Unknown')}"
    physical_cores: {self.system_info['cpu'].get('physical_cores', 4)}
    total_memory_gb: {self.system_info['memory'].get('total_gb', 8)}
    enable_avx2: {self.optimization_config['whisper_cpp'].get('enable_avx2', False)}
"""
        
        config_path = Path("config/whisper_amd_optimized.yaml")
        config_path.parent.mkdir(exist_ok=True)
        
        with open(config_path, 'w') as f:
            f.write(whisper_config)
        
        print(f"📄 Configuración AMD guardada en: {config_path}")
        return config_path

def run_full_detection():
    """Ejecutar detección completa del sistema AMD"""
    print("🔧 DETECCIÓN DE HARDWARE AMD")
    print("=" * 50)
    
    detector = AMDHardwareDetector()
    
    # Ejecutar todas las detecciones
    detector.detect_cpu()
    detector.detect_gpu()
    detector.detect_memory()
    detector.generate_amd_optimizations()
    
    # Guardar resultados
    profile_path = detector.save_system_profile()
    config_path = detector.generate_config_files()
    
    # Mostrar resumen
    print("\n" + "=" * 50)
    print("📊 RESUMEN DEL SISTEMA AMD")
    print("=" * 50)
    
    cpu_info = detector.system_info.get("cpu", {})
    gpu_info = detector.system_info.get("gpu", {})
    memory_info = detector.system_info.get("memory", {})
    
    print(f"🖥️  CPU: {cpu_info.get('model_name', 'AMD CPU')}")
    print(f"🏗️  Arquitectura: {cpu_info.get('architecture', 'Desconocida')}")
    print(f"🔢 Núcleos: {cpu_info.get('physical_cores', 'N/A')} físicos / {cpu_info.get('logical_cores', 'N/A')} lógicos")
    print(f"🎮 GPU: {gpu_info.get('series', 'AMD GPU') if gpu_info else 'No detectada'}")
    print(f"💾 RAM: {memory_info.get('total_gb', 'N/A')} GB")
    
    # Mostrar optimizaciones recomendadas
    whisper_opt = detector.optimization_config.get("whisper_cpp", {})
    print(f"\n⚡ OPTIMIZACIONES RECOMENDADAS:")
    print(f"   📊 Modelo Whisper: {whisper_opt.get('model_size', 'base')}")
    print(f"   🧵 Threads: {whisper_opt.get('threads', 4)}")
    print(f"   🎮 GPU: {'Habilitada' if whisper_opt.get('use_gpu') else 'Deshabilitada'}")
    
    return detector

if __name__ == "__main__":
    detector = run_full_detection()
    
    print(f"\n🎯 PRÓXIMOS PASOS:")
    print("1. Instalar whisper.cpp con optimizaciones AMD")
    print("2. Aplicar configuración generada")
    print("3. Ejecutar benchmarks para verificar rendimiento")
    print("4. Integrar en ProcessingAgent")

2. Script de Instalación Optimizada para AMD

#!/bin/bash
# install_whisper_amd_optimized.sh

echo "🔧 Instalación optimizada de whisper.cpp para AMD"
echo "=================================================="

# Detectar distribución
if [ -f /etc/os-release ]; then
    . /etc/os-release
    OS=$NAME
fi

echo "🐧 Distribución detectada: $OS"

# Instalar dependencias según la distribución
if [[ "$OS" == *"Ubuntu"* ]] || [[ "$OS" == *"Debian"* ]]; then
    echo "📦 Instalando dependencias para Ubuntu/Debian..."
    sudo apt update
    sudo apt install -y build-essential cmake git pkg-config
    sudo apt install -y libopenblas-dev libomp-dev
    
elif [[ "$OS" == *"Arch"* ]] || [[ "$OS" == *"Manjaro"* ]]; then
    echo "📦 Instalando dependencias para Arch/Manjaro..."
    sudo pacman -S --noconfirm base-devel cmake git pkg-config
    sudo pacman -S --noconfirm openblas openmp
    
elif [[ "$OS" == *"Fedora"* ]]; then
    echo "📦 Instalando dependencias para Fedora..."
    sudo dnf install -y gcc-c++ cmake git pkg-config
    sudo dnf install -y openblas-devel libomp-devel
fi

# Clonar whisper.cpp
echo "📥 Clonando whisper.cpp..."
git clone https://github.com/ggerganov/whisper.cpp.git
cd whisper.cpp

# Compilar con optimizaciones AMD
echo "🔨 Compilando con optimizaciones AMD..."
mkdir build
cd build

# Configurar CMake con optimizaciones para AMD
cmake -DCMAKE_BUILD_TYPE=Release \
      -DWHISPER_OPENBLAS=ON \
      -DWHISPER_OPENMP=ON \
      -DCMAKE_C_FLAGS="-march=native -mtune=native -O3" \
      -DCMAKE_CXX_FLAGS="-march=native -mtune=native -O3" \
      ..

# Compilar usando todos los núcleos disponibles
make -j$(nproc)

echo "✅ whisper.cpp compilado con optimizaciones AMD"

# Instalar globalmente
sudo cp bin/whisper /usr/local/bin/
sudo cp bin/main /usr/local/bin/whisper-main

echo "🎉 Instalación completada!"
echo "📍 Ubicación: /usr/local/bin/whisper"

3. Script de Benchmark AMD

#!/usr/bin/env python3
"""
Benchmark específico para sistemas AMD
"""

import time
import subprocess
import json
from pathlib import Path

def benchmark_amd_whisper():
    """Ejecutar benchmark optimizado para AMD"""
    
    # Cargar configuración del sistema
    profile_path = Path("system_profile_amd.json")
    if not profile_path.exists():
        print("❌ Ejecuta primero el detector AMD")
        return
    
    with open(profile_path) as f:
        profile = json.load(f)
    
    cpu_cores = profile["system_info"]["cpu"].get("physical_cores", 4)
    
    # Diferentes configuraciones para probar
    test_configs = [
        {"threads": 2, "model": "base", "name": "Conservador"},
        {"threads": cpu_cores // 2, "model": "base", "name": "Balanceado"},
        {"threads": cpu_cores, "model": "base", "name": "Máximo CPU"},
        {"threads": cpu_cores, "model": "small", "name": "Modelo Grande"}
    ]
    
    # Crear audio de prueba si no existe
    if not Path("test_audio.wav").exists():
        print("🎵 Creando audio de prueba...")
        subprocess.run([
            "espeak-ng", "-s", "150", "-v", "es",
            "-w", "test_audio.wav",
            "Esta es una prueba de rendimiento para procesadores AMD con whisper punto cpp"
        ])
    
    results = []
    
    for config in test_configs:
        print(f"\n🧪 Probando configuración: {config['name']}")
        print(f"   Threads: {config['threads']}, Modelo: {config['model']}")
        
        start_time = time.time()
        
        try:
            result = subprocess.run([
                "whisper", "test_audio.wav",
                "--threads", str(config["threads"]),
                "--model", config["model"],
                "--language", "es",
                "--output_format", "txt"
            ], capture_output=True, text=True, timeout=120)
            
            end_time = time.time()
            processing_time = end_time - start_time
            
            if result.returncode == 0:
                results.append({
                    "config": config,
                    "processing_time": processing_time,
                    "success": True
                })
                print(f"   ⏱️ Tiempo: {processing_time:.2f}s")
            else:
                print(f"   ❌ Error: {result.stderr}")
                
        except subprocess.TimeoutExpired:
            print(f"   ⏰ Timeout (>120s)")
    
    # Mostrar resultados
    print("\n📊 RESULTADOS DEL BENCHMARK AMD")
    print("=" * 40)
    
    if results:
        best_config = min(results, key=lambda x: x["processing_time"])
        
        for result in sorted(results, key=lambda x: x["processing_time"]):
            config = result["config"]
            time_taken = result["processing_time"]
            
            marker = "🏆" if result == best_config else "📈"
            print(f"{marker} {config['name']}: {time_taken:.2f}s")
            print(f"    Threads: {config['threads']}, Modelo: {config['model']}")
        
        print(f"\n🎯 CONFIGURACIÓN ÓPTIMA DETECTADA:")
        print(f"   {best_config['config']['name']}")
        print(f"   Threads: {best_config['config']['threads']}")
        print(f"   Modelo: {best_config['config']['model']}")
        print(f"   Tiempo: {best_config['processing_time']:.2f}s")
        
        # Guardar configuración óptima
        optimal_config = {
            "optimal_threads": best_config['config']['threads'],
            "optimal_model": best_config['config']['model'],
            "benchmark_time": best_config['processing_time'],
            "all_results": results
        }
        
        with open("amd_benchmark_results.json", "w") as f:
            json.dump(optimal_config, f, indent=2)
        
        print("💾 Resultados guardados en: amd_benchmark_results.json")
    
    else:
        print("❌ No se obtuvieron resultados válidos")

if __name__ == "__main__":
    benchmark_amd_whisper()

Archivos a Crear

  1. detect_amd_hardware.py - Detección completa de hardware
  2. install_whisper_amd_optimized.sh - Instalación optimizada
  3. benchmark_amd_whisper.py - Benchmark específico para AMD
  4. config/whisper_amd_optimized.yaml - Configuración auto-generada

Próximos Pasos

  1. Ejecutar detección: python detect_amd_hardware.py
  2. Instalar whisper.cpp optimizado: bash install_whisper_amd_optimized.sh
  3. Ejecutar benchmark: python benchmark_amd_whisper.py
  4. Integrar configuración óptima en ProcessingAgent

Optimizaciones AMD Específicas

CPU (Zen Architecture)

  • Compilación con -march=native -mtune=native
  • Uso óptimo de threads basado en arquitectura Zen
  • Aprovechamiento de instrucciones AVX2
  • OpenMP para paralelización

GPU (Radeon)

  • Soporte OpenCL para aceleración GPU
  • Configuración automática según serie RX
  • Fallback inteligente CPU/GPU

Memoria

  • Configuración de modelo según RAM disponible
  • Huge pages para sistemas con >16GB RAM
  • Optimización de caché según CPU

Este enfoque te dará el máximo rendimiento posible en tu sistema AMD específico.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions