fortran scientific-computing quadruple-precision

fortran - Tiempo de CPU en precisión cuádruple vs. doble



scientific-computing quadruple-precision (2)

Es interesante notar que si cambias:

sqrt_qp = sqrt(sqrt_qp) sqrt_qp = 2._qp

a

sqrt_qp = sqrt(2._qp)

¡el cálculo será más rápido!

Estoy haciendo algunas simulaciones a largo plazo en las que trato de lograr la mayor precisión posible en la solución de sistemas de ODE. Estoy tratando de averiguar cuánto tiempo toman los cálculos de precisión cuádruple (128 bits) frente a la precisión doble (64 bits). Busqué en Google un poco y vi varias opiniones al respecto: algunos dicen que tomará 4 veces más tiempo, otros 60-70 veces ... Así que decidí tomar el asunto en mis manos y escribí un sencillo programa de referencia de Fortran:

program QUAD_TEST implicit none integer,parameter :: dp = selected_int_kind(15) integer,parameter :: qp = selected_int_kind(33) integer :: cstart_dp,cend_dp,cstart_qp,cend_qp,crate real :: time_dp,time_qp real(dp) :: sum_dp,sqrt_dp,pi_dp,mone_dp,zero_dp real(qp) :: sum_qp,sqrt_qp,pi_qp,mone_qp,zero_qp integer :: i ! ============================================================================== ! == TEST 1. ELEMENTARY OPERATIONS == sum_dp = 1._dp sum_qp = 1._qp call SYSTEM_CLOCK(count_rate=crate) write(*,*) ''Testing elementary operations...'' call SYSTEM_CLOCK(count=cstart_dp) do i=1,50000000 sum_dp = sum_dp - 1._dp sum_dp = sum_dp + 1._dp sum_dp = sum_dp*2._dp sum_dp = sum_dp/2._dp end do call SYSTEM_CLOCK(count=cend_dp) time_dp = real(cend_dp - cstart_dp)/real(crate) write(*,*) ''DP sum: '',sum_dp write(*,*) ''DP time: '',time_dp,'' seconds'' call SYSTEM_CLOCK(count=cstart_qp) do i=1,50000000 sum_qp = sum_qp - 1._qp sum_qp = sum_qp + 1._qp sum_qp = sum_qp*2._qp sum_qp = sum_qp/2._qp end do call SYSTEM_CLOCK(count=cend_qp) time_qp = real(cend_qp - cstart_qp)/real(crate) write(*,*) ''QP sum: '',sum_qp write(*,*) ''QP time: '',time_qp,'' seconds'' write(*,*) write(*,*) ''DP is '',time_qp/time_dp,'' times faster.'' write(*,*) ! == TEST 2. SQUARE ROOT == sqrt_dp = 2._dp sqrt_qp = 2._qp write(*,*) ''Testing square root ...'' call SYSTEM_CLOCK(count=cstart_dp) do i = 1,10000000 sqrt_dp = sqrt(sqrt_dp) sqrt_dp = 2._dp end do call SYSTEM_CLOCK(count=cend_dp) time_dp = real(cend_dp - cstart_dp)/real(crate) write(*,*) ''DP sqrt: '',sqrt_dp write(*,*) ''DP time: '',time_dp,'' seconds'' call SYSTEM_CLOCK(count=cstart_qp) do i = 1,10000000 sqrt_qp = sqrt(sqrt_qp) sqrt_qp = 2._qp end do call SYSTEM_CLOCK(count=cend_qp) time_qp = real(cend_qp - cstart_qp)/real(crate) write(*,*) ''QP sqrt: '',sqrt_qp write(*,*) ''QP time: '',time_qp,'' seconds'' write(*,*) write(*,*) ''DP is '',time_qp/time_dp,'' times faster.'' write(*,*) ! == TEST 3. TRIGONOMETRIC FUNCTIONS == pi_dp = acos(-1._dp); mone_dp = 1._dp; zero_dp = 0._dp pi_qp = acos(-1._qp); mone_qp = 1._qp; zero_qp = 0._qp write(*,*) ''Testing trigonometric functions ...'' call SYSTEM_CLOCK(count=cstart_dp) do i = 1,10000000 mone_dp = cos(pi_dp) zero_dp = sin(pi_dp) end do call SYSTEM_CLOCK(count=cend_dp) time_dp = real(cend_dp - cstart_dp)/real(crate) write(*,*) ''DP cos: '',mone_dp write(*,*) ''DP sin: '',zero_dp write(*,*) ''DP time: '',time_dp,'' seconds'' call SYSTEM_CLOCK(count=cstart_qp) do i = 1,10000000 mone_qp = cos(pi_qp) zero_qp = sin(pi_qp) end do call SYSTEM_CLOCK(count=cend_qp) time_qp = real(cend_qp - cstart_qp)/real(crate) write(*,*) ''QP cos: '',mone_qp write(*,*) ''QP sin: '',zero_qp write(*,*) ''QP time: '',time_qp,'' seconds'' write(*,*) write(*,*) ''DP is '',time_qp/time_dp,'' times faster.'' write(*,*) end program QUAD_TEST

Resultados de una ejecución típica, después de compilar con gfortran 4.8.4 , sin ningún indicador de optimización:

Testing elementary operations... DP sum: 1.0000000000000000 DP time: 0.572000027 seconds QP sum: 1.00000000000000000000000000000000000 QP time: 4.32299995 seconds DP is 7.55769205 times faster. Testing square root ... DP sqrt: 2.0000000000000000 DP time: 5.20000011E-02 seconds QP sqrt: 2.00000000000000000000000000000000000 QP time: 2.60700011 seconds DP is 50.1346169 times faster. Testing trigonometric functions ... DP cos: -1.0000000000000000 DP sin: 1.2246467991473532E-016 DP time: 2.79600000 seconds QP cos: -1.00000000000000000000000000000000000 QP sin: 8.67181013012378102479704402604335225E-0035 QP time: 5.90199995 seconds DP is 2.11087275 times faster.

Algo debe estar pasando aquí. Mi suposición es que sqrt se calcula con gfortran través de un algoritmo optimizado, que probablemente no se haya implementado para cálculos de cuádruple precisión. Este podría no ser el caso para sin y cos , pero ¿por qué las operaciones elementales son 7.6 veces más lentas en precisión cuádruple mientras que para las funciones trigonométricas las cosas se desaceleran solo en un factor de 2? Si el algoritmo utilizado para las funciones trigonométricas fuera el mismo para precisión cuádruple y doble, también esperaría ver un aumento de siete veces en el tiempo de CPU para ellos.

¿Cuál es la desaceleración promedio en los cálculos científicos cuando se utiliza la precisión de 128 bits, en comparación con 64 bits?

Estoy ejecutando esto en un Intel i7-4771 @ 3.50GHz.


Más un comentario extendido que una respuesta, pero ...

Las CPU actuales proporcionan una gran cantidad de aceleración de hardware para la aritmética de coma flotante de doble precisión. Algunos incluso proporcionan instalaciones para una mayor precisión. Más allá de eso, estás limitado a implementaciones de software que son (como habrás notado) considerablemente más lentas.

Sin embargo, el factor exacto de esta ralentización es casi imposible de predecir en casos genéricos. Depende de su CPU (por ejemplo, qué aceleración tiene incorporada) y de la pila de software. Para la precisión doble, normalmente utiliza diferentes bibliotecas matemáticas que para la precisión cuádruple, y es posible que utilicen algoritmos diferentes para las operaciones básicas.

Para una operación / algoritmo en particular en un hardware dado usando el mismo algoritmo, probablemente pueda derivar un número, pero esto seguramente no será universalmente cierto.