Por qué las cuerdas C son lentas y cómo las solucionan los punteros

3

A C no le importan las cadenas. No precisamente.

Los trata como conjuntos de personajes. Sólo bytes en la memoria. Si desea utilizarlos de forma eficaz, necesita sugerencias. No porque sea divertido. Sino porque sin ellos, estás haciendo trabajo extra.

Una cadena en C es simplemente char str[100]. Parece un espacio para 100 caracteres. No lo es. Tiene espacio para 99 caracteres más un terminador. C utiliza cadenas terminadas en nulo. Cada cadena termina con el valor ASCII 0. Se escribe como '\0'.

Esto lo cambia todo.

Otros idiomas manejan las cadenas de manera diferente. Pascal usa un byte de longitud. Sabe exactamente cuántos caracteres están almacenados. ¿Preguntar por el largo? Devuelve ese byte. Instantáneamente.

C tiene que contar. Lee hasta que llega a “\0”. Esto hace que C sea más lento en algunos casos. Más rápido en otros. Depende de lo que estés haciendo.

No hay soporte integrado para cadenas en C. Depende de las bibliotecas. maneja la entrada y salida como gets y puts. maneja la manipulación. Algunos sistemas usan . Tienes que gestionar la memoria tú mismo. No puedes simplemente asignar una matriz a otra.

Eso es todo. Copias elemento por elemento. O usas strcpy. La función de biblioteca hace el trabajo pesado.

strcpy está en todas partes en C. Inicializa cadenas. Copia datos.

Después de ejecutar esto, s1 mantiene “hola” y s2 mantiene “hola”. La matriz almacena valores ASCII. Enteros. h es 104. e es 101. C piensa en bytes. Piensas en texto. A la máquina no le importa.

La comparación utiliza strcmp. Devuelve un número entero.

Cero significa igual. Negativo significa que la primera cadena es menor. Positivo significa que es mayor.

Existen otras funciones. strlen devuelve longitud. strcat concatena. Lea la página de manual si necesita más.

Pero, ¿cómo funcionan realmente estas funciones bajo el capó? Veamos “strlen”.

Un enfoque ingenuo se ve así:

La mayoría de los programadores de C odian esto. Parece ineficiente. Prefieren los punteros.

Puedes comprimirlo más.

Un verdadero experto probablemente podría acortarlo.

Los compilé en un MicroVAX con gcc. Sin optimización. Se ejecutó cada 20.000 veces en una cadena de 120 caracteres.

Primera versión: 12,3 segundos.
Segunda versión: 12,3 segundos.
Tercera versión: 12,9 segundos.

Los punteros no siempre ganan.

Escribe código que entiendas. La legibilidad importa más que unos pocos microsegundos. A menos que estés en un círculo cerrado. Entonces tal vez optimice.

strcpy sigue una evolución similar.

Comience con lo obvio:

Observe el <=. Copia el '\0'. Si lo saltas, la cadena no tiene fin. Longitud desconocida. Errores más tarde. Difícil de encontrar.

Esta versión es ineficiente. strlen ejecuta cada iteración. Llámalo una vez.

Ahora sugerencias.

Comprimirlo.

Limpio. Rápido. Peligroso si no controlas los límites.

Así funcionan las cuerdas C. Sin magia. Sólo memoria y sugerencias. Y mucho conteo cuidadoso.

Punteros frente a rendimiento en strcpy

Técnicamente, podrías escribir ** while (s1++ = s2++);** para manejar la copia de cadenas. La brecha de rendimiento entre las implementaciones ingenuas y las optimizadas es asombrosa.

Tome ** strcpy **. La primera versión tarda 415 segundos en copiar una cadena de 120 caracteres 10.000 veces. ¿La segunda versión? 14,5 segundos. El tercero baja a 9,8 segundos. El cuarto se sitúa en 10,3 segundos.

Esa no es una diferencia marginal. Es un impulso enorme. Los punteros proporcionan velocidad aquí porque evitan gastos innecesarios.

Tipos de retorno y punteros de cadena

El prototipo de strcpy en la biblioteca de cadenas revela su intención:

La mayoría de las funciones de cadena devuelven un puntero a una cadena. strcpy devuelve el valor de s1 como resultado. Esto permite encadenar operaciones o el uso inmediato de la cadena copiada sin una segunda búsqueda.

Eliminación de espacios en blanco iniciales sin mover datos

El uso de punteros con cadenas a menudo da como resultado mejoras definitivas en la velocidad. Puedes aprovecharlos si piensas un poco en ellos.

Supongamos que desea eliminar los espacios en blanco iniciales de una cadena. El instinto es cambiar los caracteres, sobrescribiendo los espacios en blanco. En C, puedes evitar el movimiento por completo.

Esto es mucho más rápido que la técnica del movimiento. Especialmente para cuerdas largas. No estás cambiando bytes. Simplemente estás cambiando el puntero de inicio. Los datos se quedan. La salida cambia.

Recogiendo trucos

Aprenderás muchos otros trucos con cuerdas a medida que avances. Leerás otro código. Verás cómo otros manejan la memoria.

La práctica es la clave. No hay ningún atajo para leer el código. Sólo hay que fijarse en cómo se hace y entender por qué funciona. Las ganancias de rendimiento son reales. La curva de aprendizaje es pronunciada. Pero los resultados hablan por sí solos.