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