GOOGLE ADS

domingo, 1 de mayo de 2022

¿Por qué llamar a snprintf() es tan lento?

Solución del problema


El error de GCC 88809 está cerrado, corregido para GCC10, que afecta a GCC9 y versiones anteriores.

GCC10 y versiones posteriores ya no deberían expandirse strlenen línea con basura lenta como repnz scasbcuando se compila glibc. (Consulte ¿Por qué este código usa strlen en gran medida 6,5 ​​veces más lento con las optimizaciones de GCC habilitadas? para obtener detalles sobre cómo strlense compila en las versiones de GCC afectadas. La versión que muestra usando repnz scasbparece el -O1peor de los casos).

snprintftodavía no es rápido en general (mucha sobrecarga analizando la cadena de formato y pasando varargs a través de funciones de contenedor). Pero con esa corrección de errores, con suerte debería escalar de manera similar a cadenas grandes como strcpy, o al menos como strlen+ memcpyque hace dos pasadas sobre los datos. Entonces, con grandes entradas que conducen a pasar la mayor parte de su tiempo en strlen y/o memcpy, al menos lo hará de manera eficiente. Probablemente con el tiempo dividido equitativamente entre strlen y memcpy, si internamente usa memcpy en lugar de un bucle escrito a mano. En lugar del 99 % en strlen como lo encontró con perf record, a menos que solo contara los resultados de muestra en esta función, no en las llamadas a memcpy.

Sus casos de prueba de referencia con cadenas medianas a grandes como 1000 y 10k bytes seguirán dedicando la mayor parte de su tiempo a eso, no analizando las cadenas de formato y la sobrecarga de la función varargs / wrapper, pero para tamaños pequeños esa sobrecarga dominará frente strncpya eso simplemente va directamente en copiar, encontrando la longitud a lo largo del camino.

Como se señaló en los comentarios, su strncatbucle es un antipatrón. Construir una cadena grande de esa manera vuelve a escanear la parte ya copiada cada vez, dando O(N * M)tiempo de ejecución para concatenar N cadenas de longitud M.

O en su caso, una vez que completa el destino, entonces solo cuesta O (tamaño de dst) tiempo por llamada básicamente memchr para un cero de terminación.

Para evitar esto, use strleny memcpyusted mismo para realizar un seguimiento del punto al que agregar. O si puede usar las funciones POSIX-2008, use stpcpyque devuelve el final de la copia, el lugar donde desea comenzar a copiar la siguiente. Desafortunadamente, esto no brinda una manera fácil de realizar comprobaciones de límites.

stpcpydebería ser tan rápido como strcpyen glibc, supongo que usando el mismo asm escrito a mano. Ha sido parte de glibc desde 1992.

Consulte también el valor de retorno de strcpy() sobre el diseño de API deficiente de las funciones de cadena ISO C que se remontan a una fecha muy temprana. (Separado del strncpydiseño que hace que apeste para evitar desbordamientos de búfer, sin forzar que el destino termine en cero).

No hay comentarios.:

Publicar un comentario

Flutter: error de rango al acceder a la respuesta JSON

Estoy accediendo a una respuesta JSON con la siguiente estructura. { "fullName": "FirstName LastName", "listings...