Optimización de Algoritmos Vectoriales SIMD para Volteo Horizontal de Imágenes de 24 bits con YShuffleX2Kernel en C#

Análisis Técnico

Detección de Cuellos de Botella

El enfoque YShuffleX3Kernel para volteo horizontal de imágenes de 24 bits requiere 9 instrucciones de permutación por iteración. En plataformas Avx2 sin soporte para permutaciones inter-lane, este número aumenta a 18 instrucciones, impactando significativamente el rendimiento. La reducción de instrucciones de permutación se convierte en objetivo clave para optimización.

Estrategia de Optimización

YShuffleX2Kernel permite procesar dos vectores simultáneamente, reduciendo las permutaciones necesarias. Para datos de 24 bits en registros de 128 bits (16 bytes), se procesan eficientemente 15 bytes mediante permutación, dejando 1 byte residual. Dos enfoques manejan este residual:

  • Enfoque A: Combina YShuffleX2Kernel (para índices 0 y 2) con YShuffleX3Kernel (índice central)
  • Enfoque B: Utiliza exclusivamente YShuffleX2Kernel con carga adicional de vetcores desplazados

VectorTraits implementa YShuffleX2Kernel automáticamente seleccionando instrucciones nativas (Avx512VL, AdvSimd) o emulación cuando es necesario.

Configuración de Índices

Los índices de permutación se calculan mediante ajustes vectoriales:

private static readonly Vector<byte> _indicesPermut0;
private static readonly Vector<byte> _indicesPermut1A;
private static readonly Vector<byte> _indicesPermut1B;
private static readonly Vector<byte> _indicesPermut2;

static Inicializador() {
    int anchoVector = Vector<byte>.Count;
    Span<byte> buffer = stackalloc byte[anchoVector * 3];
    // Lógica de cálculo de índices
    _indicesPermut0 = Vector.Subtract(_indicesBase0, new Vector<byte>(anchoVector));
    _indicesPermut1B = Vector.Subtract(_indicesBase1, new Vector<byte>(anchoVector / 3 * 3));
}
</byte></byte></byte></byte></byte></byte></byte></byte>

Implementación del Enfoque A

public static unsafe void VoltearVectoresA(byte* origen, int pasoOrigen, int ancho, int alto, byte* destino, int pasoDestino) {
    // Configuración inicial
    Vectors.YShuffleX2Kernel_Args(_indicesPermut0, out var args0);
    Vectors.YShuffleX3Kernel_Args(_indicesPermut1A, out var args1);
    Vectors.YShuffleX2Kernel_Args(_indicesPermut2, out var args2);
    
    // Núcleo de procesamiento
    Vector<byte> dato0 = carga[0];
    Vector<byte> dato1 = carga[1];
    Vector<byte> dato2 = carga[2];
    
    Vector<byte> temp0 = Vectors.YShuffleX2Kernel_Core(dato1, dato2, args0);
    Vector<byte> temp1 = Vectors.YShuffleX3Kernel_Core(dato0, dato1, dato2, args1);
    Vector<byte> temp2 = Vectors.YShuffleX2Kernel_Core(dato0, dato1, args2);
    
    // Almacenamiento
    almacen[0] = temp0;
    almacen[1] = temp1;
    almacen[2] = temp2;
}
</byte></byte></byte></byte></byte></byte>

Implementación del Enfoque B

public static unsafe void VoltearVectoresB(byte* origen, int pasoOrigen, int ancho, int alto, byte* destino, int pasoDestino) {
    int offsetB = _shuffleX2Offset1B;
    Vectors.YShuffleX2Kernel_Args(_indicesPermut1B, out var args1);
    
    // Carga de vectores desplazados
    Vector<byte> datoB0 = *(Vector<byte>*)((byte*)origen + offsetB);
    Vector<byte> datoB1 = *(Vector<byte>*)((byte*)origen + offsetB + Vector<byte>.Count);
    
    Vector<byte> temp1 = Vectors.YShuffleX2Kernel_Core(datoB0, datoB1, args1);
    // Resto del procesamiento similar
}
</byte></byte></byte></byte></byte></byte>

Resultados de Rendimiento

Plataforma X86

Método (.NET 8) Ancho 1024 (μs) Factor Mejora
Escalar 565.61 1.00x
YShuffleX2Kernel (A) 70.38 8.04x
YShuffleX2Kernel (B) 71.11 7.95x

Plataforma Arm

Método (.NET 8) Ancho 1024 (μs) Facter Mejora
Escalar 489.45 1.00x
YShuffleX2Kernel (A) 61.02 8.02x
YShuffleX2Kernel (B) 73.73 6.64x

Conclusiones de Rendimiento

  • En X86 sin Avx512, el Enfoque B muestra mejor rendimiento
  • Arm y X86 con Avx512 favorecen el Enfoque A
  • La versión .NET 8 muestra mejoras significativas en todas las variantes

Etiquetas: C# SIMD VectorTraits YShuffleX2Kernel Avx2

Publicado el 7-5 06:37