2011-12-20 16 views
27

Esto ahora puede ser un poco borroso, pero me lo he estado preguntando por un tiempo. Para mi conocimiento con !, se puede hacer está siendo evaluado seguro de un parámetro para un constructor de datos antes de que se construye el valor:Ventajas de los campos estrictos en los tipos de datos

data Foo = Bar !Int !Float 

A menudo he pensado que la pereza es una gran cosa. Ahora, cuando reviso las fuentes, veo campos estrictos con más frecuencia que la variante ! -less.

¿Cuál es la ventaja de esto y por qué no debo dejarlo flojo tal como es?

Respuesta

32

A menos que usted está almacenando una gran cálculo en el Int y flotar campos, sobrecarga significativa se puede construir a partir de una gran cantidad de cálculos triviales se acumulan en los procesadores sencillos. Por ejemplo, si agrega repetidamente 1 a un campo Float perezoso en un tipo de datos, usará más y más memoria hasta que fuerce el campo y lo calcule.

A menudo, desea almacenar costosos cálculos en un campo. Pero si sabe que no hará algo así antes de tiempo, puede marcar el campo de manera estricta y evitar tener que agregar manualmente seq en todas partes para obtener la eficiencia que desea.

Como un beneficio adicional, cuando se les da la bandera -funbox-strict-fields GHC desempacamos campos estrictas de tipos de datos directamente en el mismo tipo de datos, que es posible, ya que sabe que siempre serán evaluados, y por lo tanto no hay golpe seco tiene que ser asignado; en este caso, un valor de Bar contendría las palabras de la máquina que comprenden el Int y Float directamente dentro del valor de la Barra en la memoria, en lugar de contener dos punteros a los thunk que contienen los datos.

La pereza es una cosa muy útil, pero algunas veces se interpone en el camino e impide el cálculo, especialmente para campos pequeños que siempre se miran (y por lo tanto forzados) o que se modifican a menudo pero nunca con cálculos muy caros Los campos estrictos ayudan a superar estos problemas sin tener que modificar todos los usos del tipo de datos.

si es más común que los campos perezosos o no depende del tipo de código que está leyendo; no es probable que veas estructuras arbóreas funcionales que usen campos estrictos extensivamente, por ejemplo, porque se benefician enormemente de la pereza.

Digamos que usted tiene un AST con un constructor para las operaciones de infijos:

data Exp = Infix Op Exp Exp 
     | ... 

data Op = Add | Subtract | Multiply | Divide 

que no le gustaría hacer las Exp campos estricta, como la aplicación de una política como eso significaría que se evalúa todo el AST cada vez que mira el nodo de nivel superior, que claramente no es lo que quiere beneficiarse de la pereza. Sin embargo, el campo Op nunca contendrá un cálculo costoso que desee posponer para una fecha posterior, y la sobrecarga de un procesador por procesador puede ser costosa si tiene árboles de análisis anidados muy anidados. Por lo tanto, para el constructor infijo, querrá hacer que el campo Op sea estricto, pero deje los dos campos Exp vagos.

Solo se pueden desempacar los tipos de un solo constructor.

+1

¿Al igual que los AST no deberían ser tan estrictos? – Lanbo

+0

He ampliado mi respuesta con cierta elaboración usando AST como ejemplo. – ehird

4

Hay una sobrecarga asociada a la pereza - el compilador tiene que crear un golpe seco para el valor para almacenar el cómputo hasta que se necesite el resultado. Si sabe que siempre necesitará el resultado tarde o temprano, entonces puede tener sentido forzar la evaluación del resultado.

4

La pereza tiene un costo, de lo contrario, todos los idiomas lo tienen.

El costo es de 2 veces:

  1. Puede tomar más tiempo para configurar el procesador (es decir, la descripción de lo que tiene que ser calculada cuando se va a calcular el tiempo) que a hacer la operación inmediatamente.
  2. Los subprocesos no evaluados que van como argumentos no estrictos a otros procesos que van como argumentos no estrictos a otros procesos, etc. usarán más y más memoria. Desafortunadamente, esos tunks también pueden contener referencias a la memoria que ya no es accesible, es decir, la memoria que podría liberarse cuando solo se evaluaría el procesador, lo que impediría que el recolector de basura hiciera su trabajo. Un ejemplo sería un thunk que se supone que actualiza un cierto valor en el árbol. Digamos que este valor es válido para otros valores de 100MB. Si ya no se hace referencia al árbol viejo, esta memoria se desperdicia siempre que no se evalúe el procesador.
5

Además de la información proporcionada por otras respuestas, tenga en cuenta:

Para mis conocimientos con !, uno puede asegurarse de que se está evaluando un parámetro para un constructor de datos antes de que se construye el valor

es interesante observar cómo deep the parameter is evaluated - es como con seq y $! evaluado a WHNF.

Teniendo en cuenta los tipos de datos

data Foo = IntFoo !Int | FooFoo !Foo | BarFoo !Bar 
data Bar = IntBar Int 

la expresión

let x' = IntFoo $ 1 + 2 + 3 
in x' 

evaluaron a WHNF produce valor IntFoo 6 (== evaluado completamente, == NF).
Además esta expresión

let x' = FooFoo $ IntFoo $ 1 + 2 + 3 
in x' 

evaluado a WHNF produce valor FooFoo (IntFoo 6) (== evaluado completamente, == NF).
Sin embargo, esta expresión

let x' = BarFoo $ IntBar $ 1 + 2 + 3 
in x' 

evaluado a WHNF produce valor BarFoo (IntBar (1 + 2 + 3)) (! = Evaluada,! = NF).

punto principal: El rigor del parámetro !Bar no necesariamente ayuda si los constructores de datos de Bar no contienen parámetros estrictos sí mismos.

+0

¡Ejemplos muy claros! ¿Puedo inspeccionar qué tan profundo se evalúa el parámetro en ghci u otras herramientas? – wenlong

+1

@wenlong Ver otra [SO respuesta sobre la visualización de thunks] (http://stackoverflow.com/questions/16767028/any-way-to-visualize-a-thunk-function-or-how-to-view-a-function -for-a-general), específicamente la herramienta ['ghc-vis'] (http://felsin9.de/nnis/ghc-vis/#basic-usage) – mucaho

Cuestiones relacionadas