Pregunta

¿Hay alternativas más rápidas a memcpy () en C ++?

¿Fue útil?

Solución

Poco probable. Su compilador / biblioteca estándar probablemente tendrá una implementación muy eficiente y personalizada de memcpy. Y memcpy es básicamente la API más baja que existe para copiar una parte de la memoria a otra.

Si desea más aceleraciones, encuentre una manera de no necesitar ninguna copia de memoria.

Otros consejos

Primero, un consejo. Suponga que las personas que escribieron su biblioteca estándar no son estúpidas. Si hubiera una forma más rápida de implementar una memoria general, lo habrían hecho.

Segundo, sí, hay mejores alternativas.

  • En C ++, use la función std :: copy . Hace lo mismo, pero es 1) más seguro y 2) potencialmente más rápido en algunos casos. Es una plantilla, lo que significa que se puede especializar para tipos específicos, por lo que es potencialmente más rápida que la memoria general C.
  • O puede usar su conocimiento superior de su situación específica. Los implementadores de memcpy tuvieron que escribirlo para que funcionase bien en cada caso. Si tiene información específica sobre la situación donde la necesita, puede escribir una versión más rápida. Por ejemplo, ¿cuánta memoria necesitas copiar? ¿Cómo está alineado? Eso podría permitirle escribir una memoria más eficiente para este caso específico. Pero no será tan bueno en la mayoría de los otros casos (si es que funciona)

El experto en optimización Agner Fog ha publicado funciones de memoria optimizadas: http://agner.org/optimize/#asmlib . Sin embargo, está bajo GPL.

Hace un tiempo, Agner dijo que estas funciones deberían reemplazar a los componentes incorporados de GCC porque son mucho más rápidos. No sé si se ha hecho desde entonces.

Esta respuesta para una pregunta muy similar (sobre memset () ) también se aplica aquí.

Básicamente dice que los compiladores generan un código muy óptimo para memcpy () / memset () - y un código diferente dependiendo de la naturaleza de los objetos (tamaño, alineación , etc.).

Y recuerde, solo memcpy () PODs en C ++.

Para encontrar o escribir una rutina rápida de copia de memoria, debemos entender cómo funcionan los procesadores.

Los procesadores desde Intel Pentium Pro hacen & # 8220; Ejecución fuera de orden & # 8221 ;. Pueden ejecutar muchas instrucciones en paralelo si las instrucciones no tienen dependencias. Pero este es solo el caso cuando las instrucciones operan solo con registros. Si funcionan con memoria, se utilizan unidades de CPU adicionales, llamadas & # 8220; unidades de carga & # 8221; (para leer datos de la memoria) y & # 8220; almacenar unidades & # 8221; (para escribir datos en la memoria). La mayoría de las CPU tienen dos unidades de carga y una unidad de almacenamiento, es decir, pueden ejecutar en paralelo dos instrucciones que se leen de la memoria y una instrucción que se escribe en la memoria (nuevamente, si no se afectan entre sí). El tamaño de estas unidades suele ser el mismo que el tamaño máximo de registro & # 8211; si la CPU tiene registros XMM (SSE) & # 8211; tiene 16 bytes, si tiene registros YMM (AVX) & # 8211; es de 32 bytes, y así sucesivamente. Todas las instrucciones que leen o escriben memoria se traducen en microoperaciones (microoperaciones) que van al grupo común de microoperaciones y esperan allí a que las unidades de carga y almacenamiento puedan servirlas. Una sola unidad de carga o almacenamiento solo puede servir una microoperación a la vez, independientemente del tamaño de datos que necesita cargar o almacenar, ya sea 1 byte o 32 bytes.

Entonces, la copia de memoria más rápida se movería hacia y desde registros con tamaño máximo. Para los procesadores habilitados para AVX, la forma más rápida de copiar memoria sería repetir la siguiente secuencia, desenrollada en bucle:

vmovdqa     ymm0,ymmword ptr [rcx]
vmovdqa     ymm1,ymmword ptr [rcx+20h]
vmovdqa     ymmword ptr [rdx],ymm0
vmovdqa     ymmword ptr [rdx+20h],ymm1

El código de Google publicado anteriormente por hplbsh no es muy bueno, ya que utilizan todos los registros de 8 xmm para guardar los datos antes de que comiencen a escribirlos de nuevo, mientras que no es necesario & # 8211; ya que solo tenemos dos unidades de carga y una unidad de tienda. Entonces, solo dos registros dan los mejores resultados. Usar tantos registros de ninguna manera mejora el rendimiento.

Una rutina de copia de memoria también puede usar algunos "avanzados" técnicas como & # 8220; captación previa & # 8221; para indicar al procesador que cargue memoria en la memoria caché por adelantado y & # 8220; escrituras no temporales & # 8221; (si está copiando fragmentos de memoria muy grandes y no necesita que los datos del búfer de salida se lean inmediatamente), escrituras alineadas frente a no alineadas, etc.

Los procesadores modernos, lanzados desde 2013, si tienen el bit ERMS en la CPUID, tienen el llamado & # 8220; rep mejorado movsb & # 8221 ;, por lo que para la copia de memoria grande, el & # 8220; rep movsb & # 8221 ; se puede usar & # 8211; la copia será muy rápida, incluso más rápida que con los registros ymm, y funcionará correctamente con el caché. Sin embargo, los costos iniciales de esta instrucción son muy altos & # 8211; aproximadamente 35 ciclos, por lo que paga solo en bloques de memoria grandes.

Espero que ahora sea más fácil para usted elegir o escribir la mejor rutina de copia de memoria necesaria para su caso.

Incluso puede mantener el memcpy / memmove estándar, pero obtenga su propio largememcpy especial () para sus necesidades.

Dependiendo de lo que intente hacer ... si es una memoria lo suficientemente grande, y solo está escribiendo en la copia con moderación, un mmap con MMAP_PRIVATE para crear una asignación de copia en escritura podría ser posiblemente más rápido .

Dependiendo de su plataforma, puede haber casos de uso específicos, como si supiera que el origen y el destino están alineados con una línea de caché y el tamaño es un múltiplo entero del tamaño de la línea de caché. En general, la mayoría de los compiladores producirán un código bastante óptimo para memcpy.

No estoy seguro de que usar la memoria predeterminada siempre sea la mejor opción. La mayoría de las implementaciones de memcpy que he visto tienden a intentar alinear los datos al principio, y luego hacen copias alineadas. Si los datos ya están alineados, o son bastante pequeños, entonces esto es perder el tiempo.

A veces es beneficioso tener una copia de palabras especializadas, una copia de media palabra, una memoria de copia de bytes, siempre que no tenga un efecto demasiado negativo en los cachés.

Además, es posible que desee un control más preciso sobre el algoritmo de asignación real. En la industria de los juegos es excepcionalmente común que las personas escriban sus propias rutinas de asignación de memoria, independientemente de cuánto esfuerzo hayan gastado los desarrolladores de la cadena de herramientas en primer lugar para desarrollarla. Los juegos que he visto casi siempre tienden a usar Malloc de Doug Lea .

En términos generales, sin embargo, estaría perdiendo el tiempo tratando de optimizar la memoria, ya que sin duda habrá muchos bits de código más fáciles de acelerar en su aplicación.

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