#fortran #gfortran
#fortran #gfortran
Вопрос:
Я переписываю некоторый устаревший код, чтобы улучшить читаемость и, надеюсь, упростить его обслуживание.
Я пытаюсь уменьшить количество входных параметров для подпрограмм, но обнаружил, что изменение subroutine sub(N, ID)
—> subroutine sub(N)
заметно снизило производительность.
ID
используется только в sub
, поэтому я не считаю, что имеет смысл использовать ее в качестве входных данных. Можно ли использовать sub(N)
без снижения производительности? (Для моего использования N < 10, где производительность в 5-10 раз хуже.)
Сравнение производительности:
-
sub_1
N = 4
, 0,9 секундыN = 20
, 1,0 секундыN = 200
, 2,1 секунды
-
sub_2
N = 4
, 0,07 секундыN = 20
, 0,18 секундыN = 200
, 1,3 секунды
Я использую Mac OS 10.14.6 с gfortran 5.2.0
program test
integer, parameter :: N = 1
real, dimension(N) :: ID
call CPU_time(t1)
do i = 1, 10000000
CALL sub_1(N)
end do
call CPU_time(t2)
write ( *, * ) 'Elapsed real time =', t2 - t1
call CPU_time(t1)
do i = 1, 10000000
CALL sub_2(N, ID)
end do
call CPU_time(t2)
write ( *, * ) 'Elapsed real time =', t2 - t1
end program test
SUBROUTINE sub_1(N)
integer, intent(in) :: N
real, dimension(N) :: ID
ID = 0.0
END SUBROUTINE sub_1
SUBROUTINE sub_2(N, ID)
integer, intent(in) :: N
real, dimension(N), intent(in out) :: ID
ID = 0.0
END SUBROUTINE sub_2
Комментарии:
1. Не могли бы вы рассказать нам, как именно вы ее скомпилировали, пожалуйста?
2. Также было бы полезно иметь время в quest8n, а не в коде, я их почти пропустил
3. @IanBush Возможно, это наивно, но я просто использовал команду
gfortran test.f95
, затем выполненную с./a.out
помощью (в терминале)4. Спасибо! Пожалуйста, добавьте флаг -O3 в строку компиляции и повторите.
5. Вы также можете явно добавить
-fstack-arrays
опцию.
Ответ №1:
Похоже, это «особенность» старой версии gfortran, которую вы используете. Если я использую более поздние версии, по крайней мере, для N = 10, время намного более сопоставимо:
ian@eris:~/work/stack$ head s.f90
program test
integer, parameter :: N = 10
real, dimension(N) :: ID
call CPU_time(t1)
do i = 1, 10000000
CALL sub_1(N)
end do
ian@eris:~/work/stack$ gfortran-5 --version
GNU Fortran (Ubuntu 5.5.0-12ubuntu1) 5.5.0 20171010
Copyright (C) 2015 Free Software Foundation, Inc.
GNU Fortran comes with NO WARRANTY, to the extent permitted by law.
You may redistribute copies of GNU Fortran
under the terms of the GNU General Public License.
For more information about these matters, see the file named COPYING
ian@eris:~/work/stack$ gfortran-5 -O3 s.f90
ian@eris:~/work/stack$ ./a.out
Elapsed real time = 0.149489999
Elapsed real time = 1.99675560E-06
ian@eris:~/work/stack$ gfortran-6 --version
GNU Fortran (Ubuntu 6.5.0-2ubuntu1~18.04) 6.5.0 20181026
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
ian@eris:~/work/stack$ gfortran-6 -O3 s.f90
ian@eris:~/work/stack$ ./a.out
Elapsed real time = 7.00005330E-06
Elapsed real time = 5.00003807E-06
ian@eris:~/work/stack$ gfortran-7 --version
GNU Fortran (Ubuntu 7.4.0-1ubuntu1~18.04.1) 7.4.0
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
ian@eris:~/work/stack$ gfortran-7 -O3 s.f90
ian@eris:~/work/stack$ ./a.out
Elapsed real time = 8.00006092E-06
Elapsed real time = 6.00004569E-06
ian@eris:~/work/stack$ gfortran-8 --version
GNU Fortran (Ubuntu 8.3.0-6ubuntu1~18.04.1) 8.3.0
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
ian@eris:~/work/stack$ gfortran-8 -O3 s.f90
ian@eris:~/work/stack$ ./a.out
Elapsed real time = 9.00030136E-06
Elapsed real time = 6.00004569E-06
Однако я бы взял все вышесказанное с полным ведром соли. Более чем вероятно, что оптимизатор выяснил, что ему на самом деле ничего не нужно делать в этом простом случае, и поэтому просто избавился от всех операций, которые вы хотите засечь — единственный тест, который действительно может рассказать вам об этом, — это код, который вы хотите запустить.
Комментарии:
1. Спасибо. У меня было ощущение, что это может быть проблема с версией. Я обновлю. Я ценю помощь.
2. @NickBrady пожалуйста, ознакомьтесь с дополнительными комментариями по оптимизации компилятора, которые я только что добавил…
3. Возможности оптимизатора — очень вероятная проблема. Некоторые мысли, не было бы лучше иметь подпрограммы в отдельном файле (чтобы у компилятора было меньше шансов встроиться), а также добавить выходной аргумент, который получает значение, находящееся в цикле, например, добавленное в программную переменную (и не забудьте записать его, иначе оптимизатор компилятора снова может быть умным …). К сожалению, я не могу проверить эти идеи, поскольку у меня есть только одна версия компилятора.
Ответ №2:
sub_1
и sub_2
на самом деле не сопоставимы. В sub_1
вы выделяете ID
, инициализируете все элементы, а затем выбрасываете их, когда подпрограмма возвращается (потому что она локальна для подпрограммы).
Поскольку этот ID
массив никогда не используется, компилятор может оптимизировать его создание и инициализацию. Это то, что делает gfortran, если вы компилируете с -O3 . Сгенерированный код для sub_1
не делает ничего, кроме возврата.
В sub_2
нем все равно нужно установить все элементы ID
равными 0.0.
Комментарии:
1. С учетом этой теории имеет ли смысл, что
sub_2
это более быстрая подпрограмма?2. Без оптимизации
sub_2
, вероятно, будет быстрее, потому что ей не нужно выделять массив. Я ожидал бы, что оптимизацияsub_1
будет быстрее. Но важнее то, что они не сопоставимы, потому что они не выполняют одно и то же.3. Компилятор может выдать любой результат, он никогда не печатается.
Ответ №3:
Я предполагаю, что это связано с распределением массива.
Сам процесс выделения памяти требует времени. Когда вы передаете массив без изменений в подпрограмму sub_2
, я думаю, что очень вероятно, что подпрограмме не нужно выделять память для массива. Это может предполагать, что массивы создаются в куче, а не в стеке, но я не уверен на 100%.
С другой стороны, для подпрограммы sub_1
ей необходимо каждый раз заново выделять пространство для массива.
К сожалению, я не слишком хорошо разбираюсь в оптимизации, поэтому я надеюсь, что другие люди согласятся со мной или скажут мне, что я неправ 😉