Juanjo ha apuntado que muy probablemente esto que yo comentaba como un típico error que la gente comete cuando programa, se optimiza en los lenguajes compilados (vease C/C++/Java/…).
Lo que proponía Juanjo (de probarlo con el g++ optimizando a tope) me ha parecido buena idea, así que he hecho unas cuantas pruebas en C++.
Los resultados han sido interesantes
Para mi grata sorpresa, el g++ ha optimizado muy bien la función strlen. Sin embargo, no es capaz de optimizar “cualquier” función que tenga sus mismas características.
En el archivo test-strlen.cpp he realizado varias pruebas (más abajo están los fuentes descargables en un .tgz).
La primera prueba ha sido realizar un bucle con strlen, tal cual el código de PHP, y otro donde el strlen está fuera del bucle.
// strlen dentro del bucle
for (j = 0; j <= strlen (str); j++) {
copy [j] = str[j];
}
/* [...] */
// strlen fuera del bucle
len = strlen (str);
for (j = 0; j <= len; j++) {
copy [j] = str[j];
}
Ha resultado que el g++ ha optimizado muy bien el primer bucle, hasta el punto que ambos probablemente ejecuten el mismo código, no se aprecia diferencia.
Una vez hecho esto, me he dicho, voy a fastidiar un poco al compilador, así que he añadido una linea, que básicamente modifica str, que es la cadena que se le pasa al strlen, para que el compilador tenga que evaluar siempre el bucle:
// strlen dentro del bucle, modificando str
for (j = 0; j <= strlen (str); j++) {
copy [j] = str[j];
if (j == 1) str[j] = str[j-1];
}
El caso es que ha funcionado, básicamente el compilador no lo ha podido optimizar, no obstante esto no me vale para ejemplificar lo que decía en el artículo anterior, porque el compilador ha hecho lo que tenía que hacer, y la variable ha sido modificada dentro.
He aquí los resultados de test-strlen:
Making the common mistake(not optimizable): 9.20
Making the common mistake(optimizable by the compiler): 0.00
Avoiding the common mistake: 0.00
Total, que he creado otro archivo de ejemplo, el test-custom-strlen.cpp cuyo objetivo es implementar una función equivalente a strlen y ver si el compilador es igual de inteligente.
Básicamente, en este archivo he creado 5 funciones, vease: custom_strlen, custom_strlen_const, custom_strlen_const2, strlen_wrap, strlen_wrap_const
/** this is a custom strlen function */
size_t custom_strlen (char *str)
{
char *ptr = str;
if (ptr == NULL) return 0;
while (*ptr != '\0') ptr++;
return (size_t)(ptr - str);
}
/** this is same as above, but telling the compiler we are not touching str*/
size_t custom_strlen_const (const char *str)
{
const char *ptr = str;
if (ptr == NULL) return 0;
while (*ptr != '\0') ptr++;
return (size_t)(ptr - str);
}
/** this strlen updates input string, no matter that is const */
size_t custom_strlen_const2 (const char *str)
{
char *ptr = (char*)str;
if (ptr == NULL) return 0;
while (*ptr != '\0') ptr++;
((char*)str)[0] = 'a'; // update string
return (size_t)(ptr - str);
}
/** this is a wrapper over strlen function */
size_t strlen_wrap (char *str) {
return strlen (str);
}
/** this is a wrapper over strlen function with constant input */
size_t strlen_wrap_const (const char *str) {
return strlen (str);
}
Y los resultados ha sido los siguientes:
using custom_strlen: 3.90
using custom_strlen_const: 4.05
using custom_strlen_const2: 4.04
using strlen_wrap: 0.00
using strlen_wrap_const: 0.00
Vamos, que el compilador, con -O3, no ha sido capaz de optimizar ninguna de mis funciones. Y el caso es que la función custom_strlen_const era candidata para ser optimizada igual que el strlen (función con un parámetro constante, internamente usa const para todo y no cambia el valor de nada).
Total, que sigo manteniendo la misma opinión que en el artículo anterior: si una función se va a evaluar siempre a lo mismo, mejor que esté fuera del bucle, porque no se sabe si el compilador o intérprete va a ser capaz de optimizarla o no.
También me gustaría decir que por lo general este tipo de cosas, normalmente, tampoco va a suponer ningún cuello de botella en ninguna aplicación, ni va a suponer tiempos abismales de ejecución, por lo que no es preciso prestarle mucha importancia; ahora bien, son pequeños detalles, que se van sumando y sumando, y oye, quizá no reduzcan los tiempos un 30%, pero un 5%…
Aquí teneis el archivo comprimido con los códigos fuentes de ejemplo. (descomprimir, ejecutar make y listo)
English
No Comments on "Un error común en programación: Lenguajes compilados"