Процессорное время в четверной или двойной точности

Я делаю некоторые долгосрочные симуляции, в которых я пытаюсь достичь максимально возможной точности в решении систем ОДУ. Я пытаюсь выяснить, сколько времени занимает четырехкратное (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)

вычисление будет быстрее!

Другие вопросы по тегам