performance perl types moose typechecking

performance - ¿Vale la pena comprobar los argumentos de la función Perl?



types moose (9)

Hay un montón de rumores acerca de MooseX::Method::Signatures e incluso antes de eso, los módulos como Params::Validate están diseñados para escribir cada argumento de los métodos o funciones. Estoy considerando usar el primero para todos mis códigos Perl futuros, tanto personales como en mi lugar de trabajo. Pero no estoy seguro de si vale la pena el esfuerzo.

Estoy pensando en todo el código Perl que he visto (y escrito) antes de que no se realice tal comprobación. Rara vez veo un módulo hacer esto:

my ($a, $b) = @_; defined $a or croak ''$a must be defined!''; !ref $a or croak ''$a must be a scalar!"; ... @_ == 2 or croak "Too many arguments!";

Quizás porque simplemente es demasiado trabajo sin algún tipo de módulo auxiliar, pero tal vez porque en la práctica no enviamos argumentos en exceso a las funciones, y no enviamos referencias a los métodos que esperan escalas, o si lo hacemos, tenemos use warnings; y nos enteramos rápidamente de ello: un enfoque de tipificación de pato .

Entonces, ¿vale la pena comprobar el rendimiento del tipo de prueba de Perl, o sus fortalezas se muestran predominantemente en lenguajes compilados y tipificados como C o Java?

Me interesan las respuestas de cualquier persona que tenga experiencia escribiendo Perl que use estos módulos y haya visto beneficios (o no) de su uso; si su empresa / proyecto tiene alguna política relacionada con la verificación de tipos; y cualquier problema con la comprobación de tipos y el rendimiento.

ACTUALIZACIÓN: Leí un artículo interesante sobre el tema recientemente, llamado Prueba fuerte frente a Escritura fuerte . Ignorando el ligero sesgo de Python, esencialmente afirma que la verificación de tipos puede ser asfixiante en algunos casos, e incluso si su programa pasa las verificaciones de tipos, no es garantía de corrección; las pruebas adecuadas son la única manera de estar seguro.


Algunas veces. Generalmente lo hago siempre que paso opciones a través de hash o hashref. En estos casos, es muy fácil no recordar o escribir incorrectamente el nombre de una opción, y verificar con Params::Check puede ahorrar mucho tiempo de solución de problemas.

Por ejemplo:

sub revise { my ($file, $options) = @_; my $tmpl = { test_mode => { allow => [0,1], ''default'' => 0 }, verbosity => { allow => qw/^/d+$/, ''default'' => 1 }, force_update => { allow => [0,1], ''default'' => 0 }, required_fields => { ''default'' => [] }, create_backup => { allow => [0,1], ''default'' => 1 }, }; my $args = check($tmpl, $options, 1) or croak "Could not parse arguments: " . Params::Check::last_error(); ... }

Antes de agregar estas comprobaciones, me olvidaría si los nombres que se usan son guiones bajos o guiones, pase require_backup lugar de create_backup , etc. Y esto es para el código que escribí yo mismo: si otras personas lo van a usar, definitivamente debería hacer algo. tipo de prueba de idiotas. Params::Check facilita bastante la verificación de tipos, la verificación de valores permitidos, los valores predeterminados, las opciones requeridas, el almacenamiento de valores de opciones en otras variables y más.


Básicamente estoy de acuerdo con Brian. Cuánto necesita preocuparse por las entradas de su método depende en gran medida de cuánto le preocupa que a) alguien ingrese datos incorrectos, yb) datos incorrectos corrompan el propósito del método. También agregaría que hay una diferencia entre los métodos externos e internos. Debe ser más diligente con los métodos públicos porque le está haciendo una promesa a los consumidores de su clase; a la inversa, puede ser menos diligente con los métodos internos, ya que tiene un mayor control (teórico) sobre el código que accede a él, y solo puede culpar a usted mismo si las cosas salen mal.

MooseX :: Method :: Signatures es una solución elegante para agregar una forma declarativa simple de explicar los parámetros de un método. Method :: Signatures :: Simple y Params :: Validate están bien, pero carecen de una de las características que me parecen más atractivas de Moose: el sistema Type. He usado MooseX :: Declare y por extensión MooseX :: Method :: Signatures para varios proyectos y me parece que la barra para escribir los cheques adicionales es tan mínima que es casi seductora.


El contraargumento que he visto presentado a esto es que la verificación de parámetros en cada llamada de función es redundante y una pérdida de tiempo de CPU. Los partidarios de este argumento favorecen un modelo en el que todos los datos entrantes se verifican rigurosamente cuando ingresan por primera vez al sistema, pero los métodos internos no tienen comprobaciones de parámetros porque solo deben llamarse por código que les pasará los datos que ya han pasado las comprobaciones en el sistema. frontera, por lo que se supone que sigue siendo válido.

En teoría, me gusta mucho el sonido de eso, pero también puedo ver con qué facilidad puede caer como un castillo de naipes si alguien usa el sistema (o el sistema necesita crecer para permitir su uso) de una manera imprevista cuando el Se establece frontera inicial de validación. Todo lo que necesita es una llamada externa a una función interna y todas las apuestas están desactivadas.

En la práctica, estoy usando Moose en este momento y Moose realmente no te da la opción de omitir la validación en el nivel de atributo, más MooseX :: Declare maneja y valida los parámetros del método con menos esfuerzo que desenrollar @_ a mano, por lo que Es prácticamente un punto discutible.


Estoy usando Moose ampliamente para un proyecto OO bastante grande en el que estoy trabajando. El estricto control de tipos de Moose ha salvado mi tocino en algunas ocasiones. Lo más importante es que ha ayudado a evitar situaciones en las que los valores "undef" se pasan incorrectamente al método. Solo en esos casos solo me ahorró horas de tiempo de depuración.

El éxito de rendimiento definitivamente está ahí, pero puede ser manejado. 2 horas de uso de NYTProf me ayudaron a encontrar algunos Atributos de Moose que estaba moliendo demasiado, simplemente recargué mi código y obtuve una mejora de rendimiento 4x.

Utilice la comprobación de tipos. La codificación defensiva vale la pena.

Patricio.


Params :: Validate funciona muy bien, pero, por supuesto, la comprobación de args ralentiza las cosas. Las pruebas son obligatorias (al menos en el código que escribo).


Quiero mencionar dos puntos aquí. Las primeras son las pruebas, la segunda la pregunta de rendimiento.

1) Pruebas

Usted mencionó que las pruebas pueden hacer mucho y que las pruebas son la única manera de asegurarse de que su código sea correcto. En general, diría que esto es absolutamente correcto. Pero las pruebas solo resuelven un problema.

Si escribe un módulo, tiene dos problemas o, digamos, dos personas diferentes que usan su módulo.

Usted como desarrollador y usuario que utiliza su módulo. Las pruebas ayudan con lo primero que su módulo es correcto y hace lo correcto, pero no ayudó al usuario que solo usa su módulo.

Para el último, tengo un ejemplo. Había escrito un módulo usando Moose y algunas otras cosas, mi código terminaba siempre en una falla de Segmentación. Entonces comencé a depurar mi código y buscar el problema. Paso alrededor de 4 horas de tiempo para encontrar el error. Al final, el problema fue que usé Moose con el rasgo Array. Utilicé la función "mapa" y no proporcioné una función de subrutina, solo una cadena o algo más.

Claro que fue un error absolutamente estúpido mío, pero dedico mucho tiempo a depurarlo. Al final, solo una comprobación de la entrada de que el argumento es un subref le costaría al desarrollador 10 segundos de tiempo, y me costaría mucho más tiempo a mí y posiblemente a otro.

También sé de otros ejemplos. Había escrito un cliente REST en una interfaz completamente OOP con Moose. Al final, siempre recuperaste los objetos, puedes cambiar los atributos, pero seguro que no llamó a la API REST por cada cambio que hiciste. En su lugar, cambia sus valores y al final llama a un método update () que transfiere los datos y cambia los valores.

Ahora tenía un usuario que luego escribió:

$obj->update({ foo => ''bar'' })

Seguro que recibí un error, esa actualización () no funciona. Pero seguro que no funcionó, porque el método update () no aceptaba un hashref. Solo realiza una sincronización del estado real del objeto con el servicio en línea. El código correcto sería.

$obj->foo(''bar''); $obj->update();

Lo primero funciona porque nunca hice una comprobación de los argumentos. Y no arrojo un error si alguien da más argumentos, entonces espero. El método simplemente comienza como normal.

sub update { my ( $self ) = @_; ... }

Claro que todas mis pruebas absolutamente funcionan 100% bien. Pero manejar estos errores que no son errores también me cuesta tiempo. Y le cuesta al usuario mucho más tiempo.

Así que al final. Sí, las pruebas son la única forma correcta de garantizar que su código funciona correctamente. Pero eso no significa que la comprobación de tipos no tenga sentido. La verificación de tipos está ahí para ayudar a todos los que no son desarrolladores (en su módulo) a usar su módulo correctamente. Y le ahorra a usted ya otros el tiempo de encontrar errores de volcado.

2) Rendimiento

El resumen: no te importa el rendimiento hasta que te importa.

Eso significa que hasta que su módulo funcione lento, el rendimiento siempre es lo suficientemente rápido y no necesita preocuparse por esto. Si tu módulo realmente funciona para retardar necesitas más investigaciones. Pero para estas investigaciones debes usar un perfilador como Devel :: NYTProf para ver lo que es lento.

Y yo diría. En un 99% de lentitud no se debe a que realice la comprobación de tipos, es más su algoritmo. Realiza muchos cálculos, llama a funciones a menudo, etc. A menudo, es útil si utiliza otras soluciones para utilizar otro algoritmo mejor, hacer caché u otra cosa, y el impacto en el rendimiento no es su tipo de comprobación. Pero incluso si la comprobación es el golpe de rendimiento. Entonces solo remuévelo donde importa.

No hay razón para dejar la comprobación de tipo donde el rendimiento no importa. ¿Crees que la verificación de tipos importa en un caso como el anterior? ¿Dónde he escrito un cliente REST? El 99% de los problemas de rendimiento aquí son la cantidad de solicitud que va al servicio web o el tiempo para dicha solicitud. No usar la comprobación de tipos o MooseX :: Declare, etc., probablemente no aceleraría absolutamente nada.

E incluso si ves desventajas de rendimiento. A veces es aceptable. Porque la velocidad no importa o, a veces, algo te da un mayor valor. DBIx :: Class es más lento que SQL puro con DBI, pero DBIx :: Class le da mucho por esto.


Sí, vale absolutamente la pena, porque ayudará durante el desarrollo, mantenimiento, depuración, etc.

Si un desarrollador envía accidentalmente los parámetros incorrectos a un método, se generará un mensaje de error útil, en lugar de que el error se propague a otra parte.


Sí, vale la pena: la programación defensiva es una de esas cosas que siempre valen la pena.


Si es importante para usted comprobar que un argumento es exactamente lo que necesita, vale la pena. El rendimiento solo importa cuando ya tienes el correcto funcionamiento. No importa qué tan rápido pueda obtener una respuesta incorrecta o un volcado de datos. :)

Ahora, eso suena como una estupidez, pero considere algunos casos donde no lo es. ¿Realmente me importa lo que hay en @_ aquí?

sub looks_like_a_number { $_[0] !~ //D/ } sub is_a_dog { eval { $_[0]->DOES( ''Dog'' ) } }

En esos dos ejemplos, si el argumento no es lo que usted espera, todavía obtendrá la respuesta correcta porque los argumentos no válidos no pasarán las pruebas. Algunas personas lo ven como algo feo, y puedo ver su punto de vista, pero también creo que la alternativa es fea. ¿Quién gana?

Sin embargo, habrá momentos en que necesite condiciones de guardia porque su situación no es tan simple. Lo próximo a lo que debe pasar sus datos es que puede esperar que estén dentro de ciertos rangos o de ciertos tipos y que no falle con elegancia.

Cuando pienso en las condiciones de la guardia, pienso en lo que podría suceder si las entradas son malas y cuánto me importa la falla. Tengo que juzgar eso por las exigencias de cada situación. Sé que eso apesta como una respuesta, pero tiendo a que me guste más que un enfoque de esclavitud y disciplina donde tienes que pasar por todo el lío, incluso cuando no importa.

Temo a Params::Validate porque su código es a menudo más largo que mi subrutina. El material de Moose es muy atractivo, pero debes darte cuenta de que es una forma de declarar lo que quieres y aún así puedes obtener lo que puedes construir a mano (simplemente no tienes que verlo o hacerlo). Lo que más odio de Perl es la falta de firmas de métodos opcionales, y esa es una de las características más atractivas tanto de Perl 6 como de Moose.