2010-09-28 9 views
7

Fondo rápido: tenemos una gran fuente de base escrita en Python. Es un compilador para un lenguaje específico de dominio, e internamente todo se representa como gráficos dirigidos. Estos dígrafos se crean a partir de conjuntos, por lo que usamos el tipo de conjunto incorporado en Python.¿Cómo busco un Heisenbug en algún código de Python?

El problema es que originalmente no nos dimos cuenta de que Python utiliza activamente la falta de garantía de pedido en un objeto conjunto para usar una implementación no determinista más rápida. Por lo tanto, cuando itera sobre los objetos en un conjunto (como hacemos frecuentemente), el orden en el que se devuelve es débilmente aleatorio. No cambia en cada ejecución, pero cambia con frecuencia. Por lo que he visto depurar nuestro código, parece como si un hash del código fuente actuara como una semilla para el generador de números aleatorios. Por lo tanto, al cambiar el código, incluso en una ruta que no se ejecuta, el iterador de conjunto cambia el orden en que se generan los elementos.

Nuestra estrategia básica de depuración consiste en volcar las copias en el lugar donde creemos que está el error refinándolas basado en la salida hasta que encontremos el error. No es muy elegante, pero para la mayoría de las cosas funciona en gran medida. El problema básico es que agregar/cambiar cualquier declaración de impresión desencadena un comportamiento diferente y una ruta de ejecución muy diferente. No podemos permitirnos imprimir/registrar todo, ya que ralentiza el tiempo de ejecución del compilador de aproximadamente 20 segundos (manejable) a aproximadamente una hora (no tan manejable).

¿Cómo diagnosticarías un problema que solo ocurre con poca frecuencia y desaparece cuando comienzas a buscarlo?

Editar una aclaración: Varias respuestas sugieren maneras de fijar el orden de los conjuntos. Como dice torkildr a continuación "El problema aquí no es que los conjuntos se comporten de manera extraña, el problema es que su programa se comporta como si no fuera así". Este es exactamente el problema, pero la solución no es usar conjuntos deterministas. Esto simplemente enmascararía el comportamiento. El problema es encontrar por qué nuestro programa se comporta de esta manera y corregir ese comportamiento. Los algoritmos que usamos deben funcionar en los gráficos representados como conjuntos no ordenados. Ellos no. Necesito averiguar dónde están estos insectos y por qué ocurren.

Problema resuelto: Resulta que en la implementación de Python que estoy usando (2.6 en OS-X) si la relación entre los __eq____hash__ y métodos de los objetos que se almacena en el conjunto no es una ordenación bastante válida, entonces el sistema exhibe el comportamiento débilmente aleatorio descrito. Debe haber algún código en la implementación C de set.add() que use algo del módulo aleatorio para construir la representación. Esto provoca una dependencia en el grupo de entropía del sistema que cambia el orden en las escrituras del disco.

No hay respuestas directas, pero la lectura de la pregunta de seguimiento de kriss provocó que la perspicacia resolviera este problema para que obtuviera el voto.

Respuesta

2

¿Por qué no simplemente no cambiar nada en el código que afecta al orden de salida establecido y usar pdb en lugar de agregar impresiones? ¿La configuración de un punto de interrupción también cambia el orden establecido? De lo contrario, pdb le permitirá inspeccionar las variables internas.

La descripción de su problema también conduce a algunos misterios. ¿Cómo se detecta que hay un error? Si esta detección se puede realizar en tiempo de ejecución, una posible estrategia sería ejecutar pdb desde su código (tan simple como import pdb; pdb.set_trace()) tan pronto como lo vea (utilizando if de tiempo de ejecución), no en una ejecución posterior después de cambiar el código. De esta forma no tendrías que cambiar el código para depurarlo (pero tal vez para eliminar esas afirmaciones más adelante cuando el código se depure y se fortalezca).

Por cierto, también debe probar unitario todo su código al escribirlo, entonces será mucho menos probable que oculte errores sutiles.

+0

He echado un vistazo rápido a los documentos pdb y no veo cómo hacer esto. Los documentos implican que modifica el código para insertar un punto de interrupción llamando a pdb .... ¿Lo he leído mal o simplemente no lo entiendo? – Amoss

+0

Has leído bien, pero parece que en tu caso solo tendrás que llamar a pdb desde otro módulo (o incluso desde python interactive) y luego importar y ejecutar tu módulo. Lo que no está claro es si el comportamiento establecido depende del código fuente o del estado de la memoria. Por cierto, también puedes usar dictionnaries en lugar de set. Para ellos, el orden está garantizado no aleatorio (nd. Estoy bastante sorprendido de que haya algo aleatorio en los conjuntos, pero la documentación de Python en verdad no dice nada sobre eso y parece no garantizar que el orden será estable para dos iteraciones consecutivas en conjuntos inalterados) – kriss

+0

Si tiene un módulo ejecutable, puede usar 'python -m pdb mymodule' y PDB se cargará primero, ofreciéndole la oportunidad de instalar puntos de interrupción. La mayoría de las personas modificará el código porque es más simple. –

1

Realmente no veo por qué querrías ver más el problema cuando obviamente tienes una idea de cuál es la raíz del problema.

El problema principal es: necesita gráficos ordenados deterministas; Los conjuntos de Python no te dan esto. ¿Por qué no cambiaría la implementación a alguna lista/implementación de conjunto que sí lo haga?

Cuando usted dice que cambia cada vez que se cambia el código, esto no suena del todo sorprendente, si tenemos en cuenta que el algoritmo conjunto probablemente está optimizado para utilizar el patrón de almacenamiento más rápida posible que parezca. En otras palabras, realiza otra operación, almacena una variable, imprime una línea y la memoria cambia.

editar: Resumir; El problema aquí no es que los conjuntos se comporten de manera extraña, el problema es que su programa se comporta como si no lo hiciera.

+0

.. también, sospecho que ha publicado esto, solo para que pueda escribir Heisenbug;) – torkildr

+1

Me parece que su código * no debe * tener ninguna dependencia en el pedido, pero, debido a un error, . Sin embargo, su sugerencia es probablemente la mejor manera de investigar/resolver el problema. –

+0

Porque necesitamos el rendimiento que nos da el conjunto, y si escribimos nuestros propios conjuntos determinísticos, entonces a) será demasiado lento, yb) requerirá mucho esfuerzo de desarrollo. Además, como señala Douglas anteriormente, tenemos que arreglar el error de todos modos. – Amoss

1

Raymond Hettinger ha escrito a recipe for ordered sets. Sus tiempos de ejecución de Big-O para todos los métodos son los mismos que para los conjuntos. Es posible que desee intentar cambiar todos sus conjuntos a ordersets.

Cuestiones relacionadas