El misterioso consumo de CPU del Microsoft Access 97

Resulta que hace bastante tiempo que vengo utilizando esta herramienta en el trabajo, en su versión del año 1.997, y me mosqueaba bastante que al abrir el diseño de una tabla o simplemente ver los datos de la misma, la CPU se lanzase al 100% de uso (50% en mi caso ya que tiene hyperthreading).

Para más frustración, no se puede utilizar una versión más actualizada ya que supondría muchas modificaciones en el programa de la empresa; y por supuesto Microsoft ya no da soporte para este software.

Hace algunos días me dio por investigar esto utilizando el depurador OllyDbg y me encontré con que la aplicación ejecuta un bucle prácticamente infinito para actualizar la barra de herramientas de la ventana. En este bucle en ocasiones llama (o debería llamar) al API de Windows Sleep() para liberar los recursos consumidos. Si no es así, el consumo de la CPU se mantiene muy alto aun sin estar haciendo nada.

Estar llamando continuamente a Sleep() tampoco es lo más óptimo, por tanto el punto crucial de este sistema es el cuando llamar a esta función. Los ingenieros de Microsoft programaron este bucle utilizando otro API, GetTickCount() (que devuelve el tiempo desde que se arrancó el sistema), para medir el tiempo transcurrido desde la ultima vez que se dio una vuelta al bucle.

Si nos remontamos a la época de 1.997 con los Pentium entre 200 y 400 Mhz, esta claro que debía funcionar bastante bien, pero con los años se ha creado un bug en este algoritmo. Con las nuevas máquinas de 2 y 3 Ghz el bucle tarda 0 milisegundos en ejecutarse y con por culpa de eso, nunca llama a la función Sleep().

La solución ha sido modificar la comparación que se hace al llamar a GetTickCount() por otra que salte más veces:

Parche para Microsoft Access 97

Parche para Microsoft Access 97

Tenemos el espacio bastante limitado, asi que la comprobación será sencilla. El código nuevo lo que hace es llamar a la función Sleep() si el valor que devuelve GetTickCount() es par. La función Sleep() se llama  para que espere 125 milisegundos, asi que se reducirán mucho las veces que se ejecuta el bucle.

Después de modificar la aplicación y probarla durante unos días, todo funciona correctamente, la toolbar se refresca bien y el consumo de la CPU es prácticamente 0 🙂

Actualización:

Buscando por Google, encontré este enlace a la Knowledge Base de Microsoft dando una explicación al respecto. En resumen, dicen que es normal que el access consuma el 100% durante unos 20 ~ 30 segundos después de entrar en modo reposo (tras abrir una tabla, minimizar el programa, pasar a modo diseño, etc…). Lo que no tienen en cuenta es que si trabajas activamente con el programa pasando de modo diseño a SQL, abriendo tablas y demás, se mantendrá fácilmente al 100% casi siempre.

Estoy contento con el cambio. 😉

5 comentarios

  1. Hola, no publico el parche porque podría acarrear consecuencias legales, licencias, copyrights, etc… Con la información suministrada en el post, las herramientas necesarias y un poco de práctica cualquiera puede realizar el cambio en la aplicación.

    Un saludo. 🙂

  2. Recuerdo yo que en Visual Basic 6 había un comando “Doevents” el cual simplemente da paso a otros procesos
    ¿Es más eficiente?
    ¿Tiene equivalente en la API de Windows?

    *Dada la antigüedad de la publicación sabré entender perfectamente si no contesta; ya de por sí ha hecho un logro admirable*.

  3. DoEvents acaba llamando a Sleep, que lo único que hace a su vez es procesar la lista de mensajes del sistema (GetMessage, TranslateMessage y DispatchMessage) durante un periodo de tiempo definido.
    La respuesta sería que funcionalmente son equivalentes, pero Sleep tiene menos dependencias (está en USER32.DLL y no en MSVBVM60.DLL).

Deja una respuesta

Tu dirección de correo electrónico no será publicada.