¿Por qué Math.pow (int, int) es más lento que mi ingenua implementación?

Ayer vi una pregunta preguntando por qué Math.pow(int,int) es tan lento, pero la pregunta estaba mal redactada y no mostró ningún esfuerzo de investigación, por lo que se cerró rápidamente.

Hice una pequeña prueba por mi cuenta y descubrí que el Math.powmétodo realmente funcionaba extremadamente lento en comparación con mi propia implementación ingenua (que ni siquiera es una implementación particularmente eficiente) al tratar con argumentos enteros. A continuación se muestra el código que ejecuté para probar esto:

class PowerTest {

    public static double myPow(int base, int exponent) {
        if(base == 0) return 0;
        if(exponent == 0) return 1;
        int absExponent = (exponent < 0)? exponent * -1 : exponent;
        double result = base;
        for(int i = 1; i < absExponent; i++) {
            result *= base;
        }
        if(exponent < 1) result = 1 / result;
        return result;
    }

    public static void main(String args[]) {
        long startTime, endTime;

        startTime = System.nanoTime();
        for(int i = 0; i < 5000000; i++) {
            Math.pow(2,2);
        }
        endTime = System.nanoTime();
        System.out.printf("Math.pow took %d milliseconds.\n", (endTime - startTime) / 1000000);

        startTime = System.nanoTime();
        for(int i = 0; i < 5000000; i++) {
            myPow(2,2);
        }
        endTime = System.nanoTime();
        System.out.printf("myPow took %d milliseconds.\n", (endTime - startTime) / 1000000);
    }

}

En mi computadora (Linux en una CPU Intel x86_64), la salida casi siempre informaba que Math.powtomó 10 ms mientras que myPowtomó 2 ms. Esto ocasionalmente fluctuaba por un milisegundo aquí o allá, pero Math.powcorría aproximadamente 5 veces más lento en promedio.

Investigué un poco y, según grepcode , Math.powsolo ofrece un método con la firma de tipo de (double, double), y lo difiere alStrictMath.pow método que es una llamada de método nativo.

El hecho de que la Mathbiblioteca solo ofrezca una powfunción que se ocupe de dobles parece indicar una posible respuesta a esta pregunta. Obviamente, un algoritmo de potencia que debe manejar la posibilidad de una base o exponente de tipo doble tardará más en ejecutarse que mi algoritmo que solo trata con enteros. Sin embargo, al final, se reduce a código nativo dependiente de la arquitectura (que casi siempre se ejecuta más rápido que el código de bytes JVM, probablemente C o ensamblado en mi caso). Parece que a este nivel, se realizaría una optimización para verificar el tipo de datos y ejecutar un algoritmo más simple si es posible.

Dada esta información, ¿por qué el Math.powmétodo nativo siempre funciona mucho más lento que mi myPowmétodo no optimizado e ingenuo cuando se le dan argumentos enteros?

Respuesta 1

Como han dicho otros, no puede simplemente ignorar el uso de double, ya que la aritmética de coma flotante seguramente será más lenta. Sin embargo, esta no es la única razón: si cambia su implementación para usarlos, aún es más rápido.

Esto se debe a dos cosas: la primera es que 2^2(exponente, no xor) es un cálculo muy rápido de realizar, por lo que su algoritmo está bien para eso: intente usar dos valores de Random#nextInt(o nextDouble) y verá que Math#powes En realidad mucho más rápido.

La otra razón es que llamar a métodos nativos tiene una sobrecarga, lo que en realidad es significativo aquí, porque 2^2es muy rápido de calcular y está llamando Math#powmuchas veces. Ver ¿Qué hace que las llamadas JNI sean lentas? para más sobre esto

Respuesta: 2

Math.pow es lento porque trata con una ecuación en el sentido genérico, usando potencias fraccionarias para elevarla a la potencia dada. Es la búsqueda que tiene que pasar cuando se computa lo que lleva más tiempo.

Simplemente multiplicar números juntos suele ser más rápido, ya que las llamadas nativas en Java son mucho más eficientes.

Editar: también puede ser digno de notar que las funciones matemáticas usan dobles, lo que también puede llevar una mayor cantidad de tiempo que usar ints.

Respuesta: 3

Mientras trato de crear una aplicación de Android Phonegap me sale el siguiente error: $ phonegap / lib / android / bin / create myapp opción no válida - 'e' Pruebe `jar --help 'para obtener más información que estoy usando ...

En mi programa JADE, un agente necesita enviar un mensaje ACL a otro agente. Para el agente que envía el mensaje (agent1), almacena una matriz de valores String [] que tiene que enviar. Sin embargo, para ...

Estoy tratando de implementar DFS con recursión usando el siguiente código, public static void dfs (int i, int [] [] mat, boolean [] visitada) {visitado [i] = verdadero; // Marcar nodo como "visitado" System.out ....

Java Sound ofrece instancias de FloatControl para varias funciones de línea de sonido y un tipo de control MASTER_GAIN y VOLUME. ¿Se pueden usar estos controles para cambiar el volumen del sistema?