Cualquiera puede cometer un error de Rockstar (y yo también).



Hace unos meses, apareció en las noticias un artículo asombroso [traducciones en Habré: uno y dos ] sobre Grand Theft Auto Online.



Le aconsejo que lea el artículo completo, pero en resumen, GTA Online de repente tuvo un rendimiento cuadrático al analizar un blob JSON grande (debido a múltiples llamadas strlen



); después de corregir este error, el tiempo de carga disminuyó en casi un 70%.



Esto desató un animado debate : ¿C tiene la culpa de esto? ¿O tal vez "mierda web" ? ¿O el capitalismo y sus incentivos ?



Sin embargo, todos estaban de acuerdo en una cosa : que nunca se han escrito semejante disparate.



( ¿Ya puedes sentir lo que viene? )




Uno de mis proyectos paralelos es un visor de modelos 3D de alto rendimiento llamado Erizo .





Con un código inteligente, abre un binario STL de 97 MB en una Macbook Pro 2013 en solo 165 milisegundos. Esta es una velocidad asombrosa .



Por razones de compatibilidad, también escribí un pequeño analizador para ASCII STL .



ASCII STL es un formato de texto plano mal especificado que se ve así:



solid cube_corner
          facet normal 0.0 -1.0 0.0
            outer loop
              vertex 0.0 0.0 0.0
              vertex 1.0 0.0 0.0
              vertex 0.0 0.0 1.0
            endloop
          endfacet
          facet normal 0.0 0.0 -1.0
            outer loop
              vertex 0.0 0.0 0.0
              vertex 0.0 1.0 0.0
              vertex 1.0 0.0 0.0
            endloop
          endfacet
          ...
endsolid
      
      





Escribí un analizador extremadamente robusto con un comentario como este:



/*     ASCII STL:  , 
 *   'vertex',         float. */
      
      





La carga de ASCII STL siempre me pareció un poco lenta, pero supuse que se debía a un formato de texto ineficaz.



(Las nubes se están acumulando. )






Varios eventos ocurrieron en unos días:





Aquí están los registros de descarga de 1.5MB ASCII STL con marca de tiempo (en segundos):



[erizo] (0.000000) main.c:10      | Startup!
[erizo] (0.162895) window.c:91    | Created window
[erizo] (0.162900) window.c:95    | Made context current
[erizo] (0.168715) window.c:103   | Initialized GLEW
[erizo] (0.178329) window.c:91    | Created window
[erizo] (0.178333) window.c:95    | Made context current
[erizo] (1.818734) loader.c:109   | Parsed ASCII STL
[erizo] (1.819471) loader.c:227   | Workers have deduplicated vertices
[erizo] (1.819480) loader.c:237   | Got 5146 vertices (7982 triangles)
[erizo] (1.819530) loader.c:240   | Waiting for buffer...
[erizo] (1.819624) loader.c:326   | Allocated buffer
[erizo] (1.819691) loader.c:253   | Sent buffers to worker threads
[erizo] (1.819883) loader.c:258   | Joined worker threads
[erizo] (1.819887) loader.c:279   | Loader thread done
[erizo] (1.821291) instance.c:32  | Showed window
      
      





¡Pasaron más de 1,8 segundos desde el momento del lanzamiento hasta la visualización de la ventana!



Echando un nuevo vistazo al analizador ASCII, vi que la razón es obvia:



    /*  The most liberal ASCII STL parser:  Ignore everything except
     *  the word 'vertex', then read three floats after each one. */
    const char VERTEX_STR[] = "vertex ";
    while (1) {
        data = strstr(data, VERTEX_STR);
        if (!data) {
            break;
        }

        /* Skip to the first character after 'vertex' */
        data += strlen(VERTEX_STR);

        for (unsigned i=0; i < 3; ++i) {
            SKIP_WHILE(isspace);
            float f;
            const int r = sscanf(data, "%f", &f);
            ABORT_IF(r == 0 || r == EOF, "Failed to parse float");
            if (buf_size == buf_count) {
                buf_size *= 2;
                buffer = (float*)realloc(buffer, buf_size * sizeof(float));
            }
            buffer[buf_count++] = f;

            SKIP_WHILE(!isspace);
        }
    }
      
      





Puede notar que en el código hay uno sscanf



que lee un valor flotante desde el principio del flujo de datos y cada vez verifica la longitud de toda la cadena .



Sí, cometí el mismo error que los programadores que trabajaron en GTA Online: ¡de repente escribí un analizador cuadrático!



Reemplazar una llamada sscanf



con una llamada strtof



redujo el tiempo de carga en casi 10 veces: de 1.8 segundos a 199 milisegundos.



[erizo] (0.000000) main.c:10      | Startup!
[erizo] (0.178082) window.c:91    | Created window
[erizo] (0.178086) window.c:95    | Made context current
[erizo] (0.184226) window.c:103   | Initialized GLEW
[erizo] (0.194469) window.c:91    | Created window
[erizo] (0.194472) window.c:95    | Made context current
[erizo] (0.196126) loader.c:109   | Parsed ASCII STL
[erizo] (0.196866) loader.c:227   | Workers have deduplicated vertices
[erizo] (0.196871) loader.c:237   | Got 5146 vertices (7982 triangles)
[erizo] (0.196921) loader.c:240   | Waiting for buffer...
[erizo] (0.197013) loader.c:326   | Allocated buffer
[erizo] (0.197082) loader.c:253   | Sent buffers to worker threads
[erizo] (0.197303) loader.c:258   | Joined worker threads
[erizo] (0.197306) loader.c:279   | Loader thread done
[erizo] (0.199328) instance.c:32  | Showed window
      
      








Es un recordatorio perfecto de que incluso si ha estado programando durante muchos años, siempre hay trampas . En sscanf



no llenó su tiempo la complejidad, por lo que es muy astuto el arma se disparó a sí mismo en el pie, y me parece que nadie vagaba en las tinieblas de la ignorancia.



Es posible que no se lo recuerde a usted mismo, pero cada vez que lea una historia increíble sobre un código incorrecto, recuerde: ¡también puede sucederle a usted!



(Obviamente, la moraleja de la historia es la siguiente: no use sscanf



tokens individuales desde el principio de una línea para analizar varias veces; estoy seguro de que estará bien si simplemente lo evita).






Publicidad



VDSina ofrece un VPS potente y asequible con pago diario. El canal de Internet para cada servidor es de 500 Megabits, la protección contra ataques DDoS está incluida en la tarifa, la posibilidad de instalar Windows, Linux o el SO en general desde su imagen, y también un panel de control de servidor propietario muy conveniente . ¡Intentalo!






All Articles