Процессорное время в четверной или двойной точности
Я делаю некоторые долгосрочные симуляции, в которых я пытаюсь достичь максимально возможной точности в решении систем ОДУ. Я пытаюсь выяснить, сколько времени занимает четырехкратное (128-битное) вычисление точности по сравнению с двойной (64-битной) точностью. Я немного погуглил и увидел несколько мнений по этому поводу: некоторые говорят, что это займет 4 раза дольше, другие 60-70 раз... Поэтому я решил взять дело в свои руки и написал простую программу тестирования 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
Результаты типичного прогона после компиляции с gfortran 4.8.4
без какого-либо флага оптимизации:
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.
Здесь должно что-то происходить. Я думаю, что sqrt
вычисляется с gfortran
через оптимизированный алгоритм, который, вероятно, не был реализован для вычислений четверной точности. Это может быть не так для sin
а также cos
, но почему элементарные операции в 7,6 раза медленнее с четверной точностью, в то время как для тригонометрических функций все замедляется только в 2 раза? Если бы алгоритм, используемый для тригонометрических функций, был бы одинаковым для четверной и двойной точности, я ожидал бы увидеть и семикратное увеличение времени ЦП для них.
Каково среднее замедление в научных вычислениях при использовании 128-битной точности по сравнению с 64-битной?
Я использую это на Intel i7-4771 @ 3.50GHz.
2 ответа
Больше расширенный комментарий, чем ответ, но...
Современные процессоры обеспечивают большое аппаратное ускорение для арифметики с плавающей запятой двойной точности. Некоторые даже предоставляют средства для повышенной точности. Кроме того, вы ограничены программными реализациями, которые (как вы заметили) значительно медленнее.
Однако точный фактор этого замедления почти невозможно предсказать в общих случаях. Это зависит от вашего процессора (например, в каком ускорении он встроен) и от программного стека. Для двойной точности вы обычно используете другие математические библиотеки, чем для четверной точности, и они могут использовать разные алгоритмы для основных операций.
Для конкретной операции / алгоритма на данном оборудовании, использующем тот же алгоритм, вы, вероятно, можете получить число, но это наверняка не будет универсально верным.
Интересно отметить, что если вы измените:
sqrt_qp = sqrt(sqrt_qp)
sqrt_qp = 2._qp
в
sqrt_qp = sqrt(2._qp)
вычисление будет быстрее!