Pregunta

¿Por qué un lenguaje NO usaría Evaluación de cortocircuito ? ¿Hay algún beneficio de no usarlo?

Veo que podría conducir a algunos problemas de rendimiento ... ¿es eso cierto? ¿Por qué?


Pregunta relacionada: Beneficios de usar la evaluación de cortocircuito

¿Fue útil?

Solución

Razones para NO utilizar la evaluación de cortocircuito:

  1. Porque se comportará de manera diferente y producirá resultados diferentes si sus funciones, propiedades Gets o métodos de operador tienen efectos secundarios. Y esto puede entrar en conflicto con: A) Estándares de idiomas, B) versiones anteriores de su idioma, o C) las suposiciones predeterminadas de los usuarios típicos de sus idiomas. Estas son las razones que tiene VB para no cortocircuitar.

  2. Porque es posible que desee que el compilador tenga la libertad de reordenar y podar expresiones, operadores y subexpresiones como mejor le parezca, en lugar de hacerlo en el orden en que el usuario los ingresó. Estas son las razones por las que SQL tiene para no cortocircuitos (o al menos no de la forma en que la mayoría de los desarrolladores que llegan a SQL piensan que lo haría). Por lo tanto, SQL (y algunos otros lenguajes) pueden cortocircuitarse, pero solo si así lo decide y no necesariamente en el orden que usted especificó implícitamente.

Asumo aquí que usted está preguntando acerca de & "; cortocircuito automático, implícito y específico del orden &"; que es lo que la mayoría de los desarrolladores esperan de C, C ++, C #, Java, etc. Ambos VB y SQL tienen formas de explícitamente forzar un cortocircuito específico del orden. Sin embargo, por lo general, cuando la gente hace esta pregunta, es un & "; ¡Haz lo que quiero decir &"; pregunta; es decir, quieren decir " ¿por qué no hace lo que quiero? " ;, como en, cortocircuito automático en el orden en que lo escribí.

Otros consejos

Un beneficio que se me ocurre es que algunas operaciones pueden tener efectos secundarios que podría suceder.

Ejemplo:

if (true || someBooleanFunctionWithSideEffect()) {
    ...
}

Pero eso generalmente está mal visto.

Ada no lo hace por defecto. Para forzar la evaluación de cortocircuito, debe usar and then o or else en lugar de and o or.

El problema es que hay algunas circunstancias en las que realmente ralentiza las cosas. Si la segunda condición es rápida de calcular y la primera condición es casi siempre cierta para & Quot; y & Quot; o falso para " o " ;, entonces la instrucción extra de verificación de rama es un desperdicio. Sin embargo, entiendo que con los procesadores modernos con predictores de rama, este no es el caso. Otro problema es que el compilador puede saber que la segunda mitad es más barata o es probable que falle, y puede que desee reordenar la verificación en consecuencia (lo que no podría hacer si se define el comportamiento de cortocircuito) ).

He escuchado objeciones de que puede conducir a un comportamiento inesperado del código en el caso de que la segunda prueba tenga efectos secundarios. En mi humilde opinión es sólo & "; Inesperado &"; si no conoce muy bien su idioma, pero algunos lo discutirán.

En caso de que esté interesado en lo que los diseñadores de idiomas reales tienen que decir sobre este tema, aquí hay un extracto de Ada 83 (idioma original) Justificación :

  

Los operandos de una expresión booleana   como A y B pueden evaluarse en   cualquier orden. Dependiendo de la complejidad   del término B, puede ser más   eficiente (en algunos pero no en todos   máquinas) para evaluar B solo cuando el   El término A tiene el valor VERDADERO. Esta   sin embargo es una decisión de optimización   tomado por el compilador y sería   incorrecto suponer que esto   La optimización siempre se hace. En otra   situaciones que queremos expresar un   conjunción de condiciones donde cada   condición debe ser evaluada (tiene   significado) solo si el anterior   Condición satisfecha. Ambos   las cosas se pueden hacer con cortocircuito   formas de control ...

     

En Algol 60 se puede lograr el efecto   de evaluación de cortocircuito solo por   uso de expresiones condicionales, ya que   se realiza una evaluación completa   de otra manera. Esto a menudo conduce a   construcciones que son tediosas de seguir ...

     

Varios idiomas no definen cómo   las condiciones booleanas deben ser   evaluado. Como consecuencia programas   basado en la evaluación de cortocircuito   No ser portátil. Esto claramente   ilustra la necesidad de separar   operadores booleanos de cortocircuito   formas de control.

Mira mi ejemplo en en Cortocircuito del operador booleano de SQL Server , que muestra por qué una determinada ruta de acceso en SQL es más eficiente si no se utiliza cortocircuito . El ejemplo de mi blog muestra cómo confiar realmente en el cortocircuito booleano puede romper su código si supone un cortocircuito en SQL, pero si lee el razonamiento por qué SQL está evaluando el lado derecho primero, usted Veré que es correcto y esto da como resultado una ruta de acceso mucho mejor.

Bill ha aludido a una razón válida para no usar el cortocircuito sino para deletrearlo con más detalle: las arquitecturas altamente paralelas a veces tienen problemas con las rutas de control de ramificación.

Tome la arquitectura CUDA de NVIDIA & # 8217; por ejemplo. Los chips gráficos utilizan una arquitectura SIMT, lo que significa que el mismo código se ejecuta en muchos subprocesos paralelos. Sin embargo, esto solo funciona si todos los hilos toman la misma rama condicional cada vez. Si diferentes hilos toman diferentes rutas de código, la evaluación se serializa & # 8211; lo que significa que se pierde la ventaja de la paralelización, porque algunos de los hilos tienen que esperar mientras que otros ejecutan la rama de código alternativa.

El cortocircuito en realidad implica ramificar el código, por lo que las operaciones de cortocircuito pueden ser dañinas en arquitecturas SIMT como CUDA.

& # 8211; Pero como dijo Bill, eso & # 8217; es una consideración de hardware. En cuanto a los idiomas , yo & # 8217; respondería a su pregunta con un rotundo no : evitar el cortocircuito no sentido.

Diría que 99 de cada 100 veces preferiría los operadores de cortocircuito para el rendimiento.

Pero hay dos grandes razones por las que he descubierto que no las usaré. (Por cierto, mis ejemplos están en C donde & Amp; & Amp; y || están en cortocircuito y & Amp; y | no lo están).

1.) Cuando desee llamar a dos o más funciones en una instrucción if, independientemente del valor devuelto por la primera.

if (isABC() || isXYZ()) // short-circuiting logical operator
    //do stuff;

En ese caso, solo se llama a isXYZ () si isABC () devuelve falso. Pero es posible que desee que se llame a isXYZ () pase lo que pase.

Entonces, en su lugar, haga esto:

if (isABC() | isXYZ()) // non-short-circuiting bitwise operator
    //do stuff;

2.) Cuando realiza matemáticas booleanas con enteros.

myNumber = i && 8; // short-circuiting logical operator

no es necesariamente lo mismo que:

myNumber = i & 8; // non-short-circuiting bitwise operator

En esta situación, puede obtener resultados diferentes porque el operador de cortocircuito no necesariamente evaluará la expresión completa. Y eso lo hace básicamente inútil para las matemáticas booleanas. Entonces, en este caso, usaría los operadores que no son de cortocircuito (bit a bit).

Como estaba insinuando, estos dos escenarios realmente son raros para mí. Pero puede ver que hay razones de programación reales para ambos tipos de operadores. Y afortunadamente, la mayoría de los idiomas populares actuales tienen ambos. Incluso VB.NET tiene los operadores de cortocircuito AndAlso y OrElse. Si un idioma hoy no tiene ambos, diría que está atrasado y realmente limita al programador.

Si desea evaluar el lado derecho:

if( x < 13 | ++y > 10 )
    printf("do something\n");

Quizás quisieras que se incrementara y si x < 13. Sin embargo, un buen argumento en contra de hacerlo es que crear condiciones sin efectos secundarios suele ser una mejor práctica de programación.

Como un estiramiento:

Si quisieras que un lenguaje fuera súper seguro (a costa de la genialidad), eliminarías la evaluación de cortocircuito. Cuando algo 'seguro' tarda una cantidad variable de tiempo en suceder, se puede utilizar un Timing Attack meterse con eso. La evaluación de cortocircuito hace que las cosas tarden diferentes tiempos en ejecutarse, por lo tanto, abren el agujero para el ataque. En este caso, ni siquiera permitir la evaluación de cortocircuito ayudaría a escribir algoritmos más seguros (ataques de tiempo wrt de todos modos).

El Ada el lenguaje de programación admitía tanto operadores booleanos que no producían cortocircuitos ( AND , OR ), para permitir que un compilador optimice y posiblemente paralelice las construcciones, y operadores con solicitud explícita de cortocircuito ( AND THEN , OR ELSE ) cuando eso es lo que desea el programador. La desventaja de este enfoque dual es hacer que el lenguaje sea un poco más complejo (1000 decisiones de diseño tomadas en la misma veta "¡hagamos ambas cosas!" Hará que un lenguaje de programación sea MUCHO más complejo en general; -).

No es que piense que esto es lo que está sucediendo en cualquier idioma ahora, pero sería bastante interesante alimentar ambos lados de una operación a diferentes hilos. La mayoría de los operandos podrían estar predeterminados para no interferir entre sí, por lo que serían buenos candidatos para entregarlos a diferentes CPU.

Este tipo de cosas son importantes en las CPU altamente paralelas que tienden a evaluar varias ramas y elegir una.

Oye, es un poco exagerado, pero preguntaste "¿Por qué un idioma?" ... no "¿Por qué un idioma?".

El lenguaje Lustre no utiliza la evaluación de cortocircuito. En if-then-elses, tanto las ramas then como else se evalúan en cada tick, y una se considera el resultado del condicional dependiendo de la evaluación de la condición.

La razón es que este lenguaje, y otros lenguajes de flujo de datos sincrónicos, tienen una sintaxis concisa para hablar del pasado. Cada rama necesita ser calculada para que el pasado de cada una esté disponible si es necesario en ciclos futuros. Se supone que el lenguaje es funcional, por lo que eso no importaría, pero puede llamar a las funciones C desde allí (y tal vez observe que se llaman con más frecuencia de lo que pensaba).

En Lustre, escribir el equivalente de

if (y < > 0) entonces 100 / y else 100

es un error típico de principiante. No se evita la división por cero, porque la expresión 100 / y se evalúa incluso en ciclos cuando y = 0.

Porque el cortocircuito puede cambiar el comportamiento de una aplicación IE:

if(!SomeMethodThatChangesState() || !SomeOtherMethodThatChangesState())

Yo diría que es válido para problemas de legibilidad; Si alguien aprovecha la evaluación de cortocircuito de una manera no completamente obvia, puede ser difícil para un encargado de mirar el mismo código y comprender la lógica.

Si la memoria sirve, erlang proporciona dos construcciones, estándar y / o, luego andalso / orelse. Esto aclara la intención de que 'sí, sé que esto es un cortocircuito, y usted también debería hacerlo', donde, como en otros puntos, la intención debe derivarse del código.

Como ejemplo, digamos que un mantenedor se encuentra con estas líneas:

if(user.inDatabase() || user.insertInDatabase()) 
    user.DoCoolStuff();

Se tarda unos segundos en reconocer que la intención es " si el usuario no está en la Base de Datos, insértelo / ella / él; si eso funciona, haz cosas geniales ".

Como otros han señalado, esto solo es relevante cuando se hacen cosas con efectos secundarios.

No conozco ningún problema de rendimiento, pero una posible argumentación para evitarlo (o al menos su uso excesivo) es que puede confundir a otros desarrolladores.

Ya hay excelentes respuestas sobre el problema de los efectos secundarios, pero no vi nada sobre el aspecto de rendimiento de la pregunta.

Si no permite la evaluación de cortocircuito, el problema de rendimiento es que ambas partes deben ser evaluadas aunque no cambie el resultado. Esto generalmente no es un problema, pero puede volverse relevante en una de estas dos circunstancias:

  • El código está en un bucle interno que se llama con mucha frecuencia
  • Hay un alto costo asociado con la evaluación de las expresiones (quizás IO o un cálculo costoso)

La evaluación de cortocircuito proporciona automáticamente una evaluación condicional de una parte de la expresión.

La principal ventaja es que simplifica la expresión.

El rendimiento podría mejorarse, pero también se podría observar una penalización por expresiones muy simples.

Otra consecuencia es que los efectos secundarios de la evaluación de la expresión podrían verse afectados.

En general, confiar en los efectos secundarios no es una buena práctica, pero en algún contexto específico, podría ser la solución preferida.

VB6 no utiliza la evaluación de cortocircuito, no sé si las versiones más nuevas sí, pero lo dudo. Creo que esto se debe a que las versiones anteriores tampoco lo hicieron, y porque la mayoría de las personas que usaron VB6 no esperarían que eso sucediera, y esto conduciría a la confusión.

Esta es solo una de las cosas que me hizo extremadamente difícil dejar de ser un programador novato de VB que escribió código de espagueti y continuar mi viaje para ser un programador real .

Muchas respuestas han hablado sobre los efectos secundarios. Aquí hay un ejemplo de Python sin efectos secundarios en el que (en mi opinión) el cortocircuito mejora la legibilidad.

for i in range(len(myarray)):
  if myarray[i]>5 or (i>0 and myarray[i-1]>5):
    print "At index",i,"either arr[i] or arr[i-1] is big"

El cortocircuito asegura que no intentemos acceder a myarray [-1], lo que generaría una excepción ya que las matrices de Python comienzan en 0. El código, por supuesto, podría escribirse sin cortocircuitos, por ejemplo

for i in range(len(myarray)):
  if myarray[i]<=5: continue
  if i==0: continue
  if myarray[i-1]<=5: continue
  print "At index",i,...

pero creo que la versión de cortocircuito es más legible.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top