Pregunta

Estoy realizando un procesamiento de imágenes en C que requiere copiar grandes cantidades de datos en la memoria; el origen y el destino nunca se superponen.

¿Cuál es la forma más rápida de hacer esto en la plataforma x86 usando CCG (dónde ESS, ¿SSE2 pero NO SSE3 están disponibles)?

Supongo que la solución estará en ensamblaje o utilizando intrínsecos de GCC.

Encontré el siguiente enlace pero no tengo idea de si es la mejor manera de hacerlo (el autor también dice que tiene algunos errores): http://coding.derkeiler.com/Archive/Assembler/comp.lang.asm.x86/2006-02/msg00123.html

EDITAR:tenga en cuenta que es necesaria una copia, no puedo evitar tener que copiar los datos (podría explicar por qué pero le ahorraré la explicación :))

¿Fue útil?

Solución

William Chan y Google. 30-70% más rápido que el establecimiento de memoria en Microsoft Visual Studio 2005.

void X_aligned_memcpy_sse2(void* dest, const void* src, const unsigned long size)
{

  __asm
  {
    mov esi, src;    //src pointer
    mov edi, dest;   //dest pointer

    mov ebx, size;   //ebx is our counter 
    shr ebx, 7;      //divide by 128 (8 * 128bit registers)


    loop_copy:
      prefetchnta 128[ESI]; //SSE2 prefetch
      prefetchnta 160[ESI];
      prefetchnta 192[ESI];
      prefetchnta 224[ESI];

      movdqa xmm0, 0[ESI]; //move data from src to registers
      movdqa xmm1, 16[ESI];
      movdqa xmm2, 32[ESI];
      movdqa xmm3, 48[ESI];
      movdqa xmm4, 64[ESI];
      movdqa xmm5, 80[ESI];
      movdqa xmm6, 96[ESI];
      movdqa xmm7, 112[ESI];

      movntdq 0[EDI], xmm0; //move data from registers to dest
      movntdq 16[EDI], xmm1;
      movntdq 32[EDI], xmm2;
      movntdq 48[EDI], xmm3;
      movntdq 64[EDI], xmm4;
      movntdq 80[EDI], xmm5;
      movntdq 96[EDI], xmm6;
      movntdq 112[EDI], xmm7;

      add esi, 128;
      add edi, 128;
      dec ebx;

      jnz loop_copy; //loop please
    loop_copy_end:
  }
}

Es posible que pueda para optimizarlo en función de su situación más exacta y las posibles hipótesis que son capaces de hacer.

También es posible que desee comprobar hacia fuera la fuente memcpy (memcpy.asm) y tira a su manejo de casos especiales. Puede ser posible para optimizar aún más!

Otros consejos

En cualquier nivel de optimización de -O1 o superior, GCC utilizará las definiciones incorporadas para funciones como memcpy - con el parámetro -march derecha (-march=pentium4 para el conjunto de características que mencionas) debe generar código en línea bastante óptima específica de la arquitectura

Me había referencia y ver lo que sale.

La ESS-Code Publicado por hapalibashi es el camino a seguir.

Si necesita aún más el rendimiento y no rehuir el largo y sinuoso camino de la escritura de un controlador de dispositivo: Todas las plataformas importantes hoy en día tienen un DMA-controlador que es capaz de hacer una copia-trabajo más rápido y de forma paralela a la CPU código podría hacer.

Esto implica escribir un controlador sin embargo. No es gran SO que yo sepa expone esta función a la del lado del usuario debido a los riesgos de seguridad.

Sin embargo, puede valer la pena (si necesita el rendimiento) ya que ningún código en la tierra podría superar a una pieza de hardware que está diseñado para hacer un trabajo de este tipo.

Esta pregunta tiene cuatro años y me sorprende un poco que nadie haya mencionado el ancho de banda de la memoria todavía.CPU-Z informa que mi máquina tiene RAM PC3-10700.Que la RAM tiene un ancho de banda máximo (también conocido como velocidad de transferencia, rendimiento, etc.) de 10700 MBytes/seg.La CPU de mi máquina es una CPU i5-2430M, con una frecuencia turbo máxima de 3 GHz.

En teoría, con una CPU infinitamente rápida y mi RAM, memcpy podría llegar a 5300 MBytes/seg, es decir, la mitad de 10700 porque memcpy tiene que leer y luego escribir en la RAM.(editar:Como señaló v.oddou, se trata de una aproximación simplista).

Por otro lado, imaginemos que tuviéramos una RAM infinitamente rápida y una CPU realista, ¿qué podríamos lograr?Usemos mi CPU de 3 GHz como ejemplo.Si pudiera realizar una lectura de 32 bits y una escritura de 32 bits en cada ciclo, entonces podría transferir 3e9 * 4 = 12000 MBytes/seg.Esto parece fácilmente al alcance de una CPU moderna.Ya podemos ver que el código que se ejecuta en la CPU no es realmente el cuello de botella.Ésta es una de las razones por las que las máquinas modernas tienen cachés de datos.

Podemos medir lo que realmente puede hacer la CPU comparando memcpy cuando sabemos que los datos están almacenados en caché.Hacer esto con precisión es complicado.Creé una aplicación simple que escribía números aleatorios en una matriz, los copiaba en otra matriz y luego verificaba los datos copiados.Revisé el código en el depurador para asegurarme de que el inteligente compilador no hubiera eliminado la copia.Alterar el tamaño de la matriz altera el rendimiento de la caché: las matrices pequeñas caben en la caché, las grandes no tanto.Obtuve los siguientes resultados:

  • Matrices de 40 KB:16000 MBytes/seg
  • Matrices de 400 KB:11000 MBytes/seg
  • Matrices de 4000 KB:3100 MBytes/seg

Obviamente, mi CPU puede leer y escribir más de 32 bits por ciclo, ya que 16000 es más que los 12000 que calculé teóricamente anteriormente.Esto significa que la CPU es un cuello de botella aún menor de lo que pensaba.Utilicé Visual Studio 2005 y, al ingresar a la implementación estándar de memcpy, puedo ver que usa la instrucción movqda en mi máquina.Supongo que esto puede leer y escribir 64 bits por ciclo.

El bonito código que hapalibashi publicó alcanza 4200 MBytes/seg en mi máquina, aproximadamente un 40% más rápido que la implementación de VS 2005.Supongo que es más rápido porque utiliza la instrucción de captación previa para mejorar el rendimiento de la caché.

En resumen, el código que se ejecuta en la CPU no es el cuello de botella y ajustar ese código solo logrará pequeñas mejoras.

Si específico para los procesadores de Intel, que podría beneficiarse de IPP . Si sabe que va a funcionar con una GPU Nvidia tal vez usted podría utilizar CUDA - en tanto casos puede ser mejor buscar más ancha que la optimización de memcpy () - que proporcionan oportunidades para mejorar su algoritmo en un nivel superior. Ambos son sin embargo dependiente de hardware específico.

Si estás en Windows, utilizar las API de la DirectX , que cuenta específica GPU -optimized rutinas de tratamiento de gráficos (con qué rapidez puede ser? Su CPU no está cargado. Haga otra cosa mientras se mastica la GPU).

Si quieres ser OS agnóstico, prueba a OpenGL .

No juegue con ensamblador, ya que es muy probable que usted va a fracasar miserablemente para superar a los ingenieros de software + biblioteca de decisiones con dominio de 10 años.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top