es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Cómo autovectorizar un bucle con acceso paso 2 con g++ sin OpenCL o intrínsecos.

Estoy tratando de convertir una función de una implementación que utiliza intrínsecos en C++ estándar (para simplificar el mantenimiento, la portabilidad, etc.). Todo funcionó bien, excepto por un bucle con un paso de 2 donde los bytes en posiciones impares se recopilan en una ubicación y los bytes en posiciones pares se recopilan en otra ubicación.

Se han abordado preguntas relacionadas utilizando OpenCL o intrínsecos, pero me gustaría seguir con C++ estándar.

Un ejemplo mínimo de lo que estoy tratando de auto-vectorizar sería algo como esto:

void f(const unsigned char *input, const unsigned size, unsigned char *output) {
  constexpr unsigned MAX_SIZE = 2000;
  unsigned char odd[MAX_SIZE / 2];
  unsigned char even[MAX_SIZE / 2];
  for (unsigned i = 0; size > i; ++i) {
    if (0 == i % 2) {even[i/2] = input[i];}
    else {odd[i/2] = input[i];}
  }
  //for (unsigned i = 0; size > i; i+=2) {
  //  even[i/2] = input[i];
  //  odd[i/2] = input[i+1];
  //}
  for (unsigned i = 0; size / 2 > i; ++i)
  {
    output[i] = (even[i] << 4) | odd[i];
  }
}

Compilando con g++-11.2, la salida de -fopt-info-vec-missed es:

minimal.cpp:6:29: missed: couldn't vectorize loop
minimal.cpp:6:29: missed: not vectorized: control flow in loop.

Si cambio la implementación a la que está comentada en el código, g++ no logra vectorizarlo porque:

minimal.cpp:11:29: missed: couldn't vectorize loop
minimal.cpp:13:24: missed: not vectorized: not suitable for gather load _13 = *_11;

Teniendo en cuenta que es muy sencillo implementar esto con instrucciones de mezcla de bytes empaquetados, me sorprende que g++ no pueda hacerlo.

¿Hay alguna forma de reescribir el bucle para que g++ pueda vectorizarlo?

Tags:  , , ,

Answer

  1. Avatar for davy.ai

    Desafortunadamente, parece que la versión actual de g++ (11.2) no puede vectorizar el bucle en el código proporcionado. Los mensajes de error sugieren que el flujo de control y las operaciones de carga de recolección dentro del bucle están impidiendo la vectorización.

    Una posible solución sería utilizar instrucciones de mezcla de bytes empaquetados, como se mencionó en la pregunta. Esta aproximación se podría implementar utilizando instrucciones intrínsecas, pero el objetivo es adherirse al estándar de C++.

    Otra opción sería desenrollar manualmente el bucle y utilizar declaraciones condicionales para asignar valores directamente a las matrices de elementos pares e impares, en lugar de utilizar cargas de recolección. Esto eliminaría la necesidad de cargas de recolección y permitiría la vectorización. Aquí tienes un ejemplo de cómo podría verse el código modificado:

    void f(const unsigned char *input, const unsigned size, unsigned char *output) {
      constexpr unsigned MAX_SIZE = 2000;
      unsigned char odd[MAX_SIZE / 2];
      unsigned char even[MAX_SIZE / 2];
      for (unsigned i = 0; size > i; i+=2) {
        even[i/2] = input[i];
        if (size > i+1) {
          odd[i/2] = input[i+1];
        } else {
          odd[i/2] = 0; // o cualquier otro valor predeterminado
        }
      }
      for (unsigned i = 0; size / 2 > i; ++i) {
        output[i] = (even[i] << 4) | odd[i];
      }
    }
    

    Ten en cuenta que esta aproximación asume que size siempre es par. Si size es impar, el último byte del array input no se procesaría. En este caso, puedes descartar el último byte o manejarlo por separado fuera del bucle.

Comments are closed.