Acabo de usar ~ 1 mil millones como el recuento de un z-index
CSS, y estaba pensando en las comparaciones que deben continuar. ¿Hay alguna diferencia en el rendimiento en el nivel de ALU en las comparaciones entre números muy grandes y muy pequeños?
Por ejemplo, ¿sería uno de estos dos fragmentos más caro que el otro?
snippet 1
for (int i = 0; i < 10000000; i++){
if (i < 10000000000000) {
//do nothing
}
}
snippet 2
for (int i = 0; i < 10000000; i++){
if (i < 1000) {
//do nothing
}
}
performance
cpu
Visir
fuente
fuente
CMP
instrucción individual de la máquina será más lenta sii
es mayor.Respuestas:
Todos los procesadores en los que he trabajado hacen una comparación restando uno de los operandos del otro, descartando el resultado y dejando solo los indicadores del procesador (cero, negativo, etc.). Debido a que la sustracción se realiza como una sola operación, el contenido de los operandos no importa.
La mejor manera de responder a la pregunta con certeza es compilar su código en ensamblador y consultar la documentación del procesador de destino para obtener las instrucciones generadas. Para las CPU Intel actuales, ese sería el Manual del desarrollador de software de arquitecturas Intel 64 e IA-32 .
La descripción de la
CMP
instrucción ("comparar") se encuentra en el volumen 2A, página 3-126 o página 618 del PDF, y describe su funcionamiento como:Esto significa que el segundo operando se extiende con signo si es necesario, se resta del primer operando y el resultado se coloca en un área temporal en el procesador. Luego, los indicadores de estado se configuran de la misma manera que lo serían para la
SUB
instrucción ("restar") (página 1492 del PDF).No hay mención en el
CMP
oSUB
documentación que los valores de los operandos tienen alguna relación con la latencia, por lo que cualquier valor que se utiliza es seguro.fuente
Es muy poco probable, a menos que pasar de un número pequeño a un número grande cambie su tipo numérico, digamos de
int
a along
. Incluso entonces, la diferencia podría no ser significativa. Es más probable que vea una diferencia si su lenguaje de programación cambia silenciosamente a una aritmética de precisión arbitraria debajo de las cubiertas.No obstante, su compilador particular podría estar realizando algunas optimizaciones inteligentes que no conoce. La forma de averiguarlo es medir. Ejecute un generador de perfiles en su código; ver qué comparaciones toman más tiempo. O simplemente iniciar y detener un temporizador.
fuente
Muchos procesadores tienen instrucciones "pequeñas" que pueden realizar operaciones aritméticas, incluidas comparaciones, en ciertos operandos especificados de inmediato. Los operandos que no sean esos valores especiales deben usar un formato de instrucción más grande o, en algunos casos, deben usar una instrucción de "cargar el valor de la memoria". En el conjunto de instrucciones ARM Cortex-M3, por ejemplo, hay al menos cinco formas de comparar un valor con una constante:
La primera forma es la más pequeña; la segunda y tercera forma pueden o no ejecutarse tan rápido, dependiendo de la velocidad de la memoria desde la cual se obtiene el código. La cuarta forma casi seguramente será más lenta que las tres primeras, y la quinta forma será aún más lenta, pero esta última se puede usar con cualquier valor de 32 bits.
En los procesadores x86 más antiguos, las instrucciones de comparación de forma corta se ejecutarían más rápido que las de forma larga, pero muchos procesadores más nuevos convertirán tanto las formas largas como las cortas en la misma representación cuando se recuperan por primera vez, y almacenarán esa representación uniforme en el caché. Por lo tanto, si bien los controladores integrados (como los que se encuentran en muchas plataformas móviles) tendrán una diferencia de velocidad, muchas computadoras basadas en x86 no.
Tenga en cuenta también que en muchos casos donde una constante se usa mucho dentro de un bucle, un compilador solo necesitará cargar la constante en un registro una vez, antes de que comience el bucle, haciendo que las distinciones de temporización sean discutibles. Por otro lado, hay algunas situaciones, incluso en pequeños bucles, donde eso no siempre sucederá; Si un bucle es pequeño pero muy ejecutado, ocasionalmente puede haber un rendimiento importante entre las comparaciones que involucran valores cortos e inmediatos y los que involucran valores más largos.
fuente
La respuesta corta a esta pregunta es, no , no hay diferencia de tiempo para comparar dos números en función de la magnitud de esos números, suponiendo que estén almacenados en el mismo tipo de datos (por ejemplo, entradas de 32 bits o longitudes de 64 bits).
Además, hasta el tamaño de la palabra de la ALU , es increíblemente improbable que comparar dos enteros entre sí lleve más de 1 ciclo de reloj, ya que esta es una operación trivial equivalente a una resta. Creo que todas las arquitecturas con las que he tratado tenían una comparación de enteros de ciclo único.
Los únicos casos en los que puedo pensar que he encontrado donde una comparación de dos números no era una operación de ciclo único son los siguientes:
fuente
La respuesta de @ RobertHarvey es buena; Considere esta respuesta como un complemento de la suya.
También debe considerar la predicción de rama :
Básicamente, en su ejemplo, si la
if
declaración dentro del ciclo siempre devuelve la misma respuesta, entonces el sistema puede optimizarla adivinando correctamente en qué dirección se bifurcará. En su ejemplo, debido a que laif
declaración en el primer caso siempre devuelve el mismo resultado, se ejecutará un poco más rápido que el segundo caso.Excelente pregunta de desbordamiento de pila sobre el tema
fuente
Depende de la implementación, pero sería muy, muy poco probable .
Admito que no he leído los detalles de implementación de los distintos motores del navegador, y CSS no especifica ningún tipo particular de almacenamiento para los números. Pero creo que es seguro asumir que todos los principales navegadores están utilizando números de coma flotante de doble precisión de 64 bits ("dobles", para tomar prestado un término de C / C ++) para manejar la mayoría de sus necesidades numéricas en CSS , porque esto es lo que JavaScript usa para los números, por lo que usar el mismo tipo facilita la integración.
Desde el punto de vista de la computadora, todos los dobles llevan la misma cantidad de datos: 64 bits, ya sea que el valor sea 1 o -3.14 o 1000000 o 1e100 . La cantidad de tiempo que lleva hacer una operación con estos números no depende del valor real de esos números, porque siempre está trabajando en la misma cantidad de datos. Hay una compensación en hacer las cosas de esta manera, ya que los dobles no pueden representar con precisión todos los números (o incluso todos los números dentro de su rango), pero pueden acercarse lo suficiente para la mayoría de los asuntos, y el tipo de cosas que CSS no hace numéricamente Lo suficientemente exigente como para necesitar más precisión que eso. Combine esto con los beneficios de la compatibilidad directa con JavaScript, y tendrá un argumento bastante sólido para los dobles.
No es imposible que alguien pueda implementar CSS usando una codificación de longitud variable para números. Si alguien utiliza una codificación de longitud variable, a continuación, comparando contra un pequeño número sería menos costoso que la comparación contra un gran número, ya que grandes números tienen más datos para crujir . Estos tipos de codificaciones pueden ser más precisos que los binarios, pero también son mucho más lentos, y para CSS en particular, las ganancias de precisión probablemente no sean suficientes para que valga la pena el rendimiento. Me sorprendería mucho saber que cualquier navegador hizo las cosas de esta manera.
Ahora, en teoría, hay una posible excepción a todo lo que he dicho anteriormente: comparar con cero es a menudo más rápido que comparar con otros números . Esto no se debe a que cero es corto (si ese fuera el motivo, entonces 1 debería ser igual de rápido, pero no lo es). Es porque cero te permite hacer trampa. Es el único número donde todos los bits están apagados, por lo que si sabe que uno de los valores es cero, ni siquiera tiene que mirar el otro valor como un número: si alguno de los bits está encendido, entonces no es igual a cero, y luego solo tiene que mirar un bit para ver si es mayor o menor que cero.
fuente
Si este código se interpretara cada vez que se ejecutara, habría una diferencia, ya que lleva más tiempo tokenizar e interpretar en
10000000000000
comparación1000
. Sin embargo, esta es la primera optimización obvia de los intérpretes en este caso: tokenizar una vez e interpretar los tokens.fuente