Файл: А. Б. Шнейвайс основы программирования.pdf

ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 06.12.2023

Просмотров: 321

Скачиваний: 6

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.

12.3
ФОРТРАН-подпрограмма CPU_TIME.
CPU_TIME – встроенная подпрограмма, возвращающая через свой аргумент типа real значение процессорного времени в секундах, про- шедшее к моменту ее вызова от начала запуска программы.
program tsttime4; implicit none; real t0, t1, t2, t3, t4, t5, t6
integer i, n call cpu_time(t0)
n=1000000000;
call cpu_time(t1)
do i=1,n; enddo; call cpu_time(t2)
do i=1,n; enddo; call cpu_time(t3)
n=2000000000;
call cpu_time(t4)
do i=1,n; enddo; call cpu_time(t5)
write(*,1000)
write(*,1001) t1, t2, t2-t1
write(*,1002) t2, t3, t3-t2
write(*,1003) t4, t5, t5-t4
write(*,*) ’ введи число.’
read(*,*) i; write(*,*) ’i=’,i; call cpu_time(t6)
write(*,1004) t0, t6, t6-t0 1000 format(1x,15x,’
Момент
Момент
Время ’/&
&
1x,15x,’
запуска завершения исполнения’)
1001 format(1x,’ Первого цикла
:’, f7.3, 5x, f7.3, 7x, f7.3)
1002 format(1x,’ Второго цикла :’, f7.3, 5x, f7.3, 7x, f7.3)
1003 format(1x,’ Третьего цикла :’, f7.3, 5x, f7.3, 7x, f7.3)
1004 format(1x,’ Всего расчета
:’, f7.3, 5x, f7.3, 7x, f7.3)
end
$ gfortran tsttime4.f90
$ time ./a.out
Момент
Момент
Время запуска завершения исполнения
Первого цикла
:
0.001 2.620 2.618
Второго цикла :
2.620 5.193 2.574
Третьего цикла :
5.193 10.327 5.134
введи число.
i=
5
Всего расчета
:
0.001 10.327 10.326
real
0m23.974s user
0m10.327s sys
0m0.002s
В качестве синонима CPU_TIME иногда используется имя SECOND.
363

12.4
ФОРТРАН-подпрограмма DATA_AND_TIME.
Подпрограмма DATE_AND_TIME([date] [,time] [,zone] [,values])
(см. [3, 4]) возвращает дату и время по встроенным системным часам.
Здесь квадратные скобки – не синтаксическая конструкция ФОРТРА-
На, а напоминание о необязательности заключенного в них аргумента.
Первые три аргумента — текстовые скалярные переменные:
• date – в первых восьми символах дата в виде CCYYMMDD, где
CC – век, YY – год, MM – месяц, DD – день.
• time – в первых десяти символах время в виде HHMMSS.SSS, где
HH – часы, MM – минуты, SS – секунды, SSS – миллисекунды.
• zone – в первых пяти символах разность между местным временем и средним по Гринвичу в виде SHHMM, где S – знак (+ или -),
HH – часы, MM – минуты.
• values – вектор целого типа (в в первых восьми элементах после- довательность значений: год, месяц, день, разницу во времени по отношению ко Гринвичу, час, минуты, секунды, миллисекунды.
Подпрограмма DATE_AND_TIME сама определяет все свои аргу- менты. Применим её для решения задачи из предыдущего пункта.
program tsttime6; implicit none; real(8) timer, t0, t1, t2, t3, t4, t5, t6
integer i, n t0=timer(); n=1000000000;
t1=timer()
do i=1,n; enddo; t2=timer()
do i=1,n; enddo; t3=timer()
n=2000000000;
t4=timer()
do i=1,n; enddo; t5=timer()
write(*,1000)
write(*,1001) t1, t2, t2-t1; write(*,1002) t2, t3, t3-t2
write(*,1003) t4, t5, t5-t4
write(*,*) ’ введи число ’; read (*,*) i; write(*,*) ’ i=’,i t6=timer(); write(*,1004) t0, t6, t6-t0 1000 format(1x,19x,’
Момент
Момент
Время ’/&
&
1x,19x,’
запуска завершения исполнения’)
1001 format(1x,’ Первого цикла
:’, 3x, f10.3, 3x, f10.3, 3x, f10.3)
1002 format(1x,’ Второго цикла :’, 3x, f10.3, 3x, f10.3, 3x, f10.3)
1003 format(1x,’ Третьего цикла :’, 3x, f10.3, 3x, f10.3, 3x, f10.3)
1004 format(1x,’ Всего расчета
:’, 3x, f10.3, 3x, f10.3, 3x, f10.3)
end
364

function timer()
! Именно функция timer() содержит обращение real(8)
:: timer
! к DATA_AND_TIME.
integer(4) :: ival(8)
call date_and_time(values=ival)
timer=dble(ival(8))*0.001_8+dble(ival(7))+dble(ival(6))*60.0_8+&
&
dble(ival(5))*3600.0_8
end function timer
Компиляция этой программы в режиме оптимизации -O1 и последую- щий пропуск исполнимого файла привели к следующему результату:
$ gfortran -O1 tsttime6.f
// Задание ключа оптимизации -O1 при компиляции.
$ time ./a.out
//
Момент
Момент
Время запуска завершения исполнения
Первого цикла
:
61883.424 61883.818 0.394
Второго цикла :
61883.818 61884.176 0.358
Третьего цикла :
61884.176 61884.891 0.715
введи число i=
5
Всего расчета
:
61883.424 61910.404 26.980
real
0m26.983s user
0m1.468s sys
0m0.002s
Замечания:
1. До сих пор мы в ФОРТРАНе не встречали у подпрограмм аргумен- тов, которые необязательно указывать при обращении и не знаем,
как следует описывать формальные аргументы своих собственных подпрограмм, чтобы снабдить их аналогичным свойством (служеб- ное слово OPTIONAL).
2. Кроме того, на протяжении данного курса мы при указании фак- тического аргумента никогда ещё не помещали слева от него через значок присваивания имя соответствующего формального аргумен- та (см. в функции time() вызов call date_and_time(values=ival)).
3. На примере вызова подпрограммы DATE_AND_TIME, узнали,
что современный ФОРТРАН предоставляет упомянутые в замеча- ниях возможности. В дальнейшем познакомимся с ними на практи- ке. Указанные возможности бывают иногда удобны и востребованы.
365

4. Возможность иметь необязательные аргументы полезна тогда, когда соответствующие формальные аргументы имеют значения, устанав- ливаемые по умолчанию, но при этом важно, чтобы они всё-таки были аргументами, а не локальными объектами процедуры, так как иногда при вызове процедуры нужны значения отличные от зна- чений режима умолчания. В то же время, как правило, процедура работает в режиме умолчания этих аргументов, когда видеть их в списке аргументов вызова неудобно.
5. Возможность записи аргумента процедуры через значок присваива- ния (так называемая ключевая форма задания аргумента) тоже имеет некоторые преимущества по сравнению с привычной пози- ционной формой записи аргументов (т.е. когда позиция аргумента однозначно определяет его смысловую нагрузку). Например, клю- чевая форма (в отличие от позиционной) позволяет при вызове про- цедуры располагать аргументы в любом порядке. Цена — несколько более длинная запись списка аргументов.
6. При ключевой форме задания слева от знака присваивания пишет- ся имя формального аргумента, а справа — имя фактического, что,
собственно, и позволяет по имени формального аргумента распо- знать смысловую нагрузку фактического, не требуя соблюдения по- рядка записи аргументов (подробности см., например, в [3] пункт
16.4.4 “Ключевые и необязательные параметры” ).
366


12.5
ФОРТРАН-подпрограмма SYSTEM_CLOCK
Подпрограмма system_clock([count] [,count_rate] [, count_max])
возвращает через аргументы характеристики системного таймера:
1. count – текущее значение таймера увеличивается на единицу при каждом отсчете пока не достигнет значения count_max после чего начинается новый цикл отсчета времени (cм., [3]).
2. count_rate: число отсчетов таймера в секунду.
3. count_max – максимальное значение таймера.
program tsttime7; implicit none; integer i, n, m, tr, tm integer t0, t1, t2, t3, t4, t5, t6
call system_clock(count=t0, count_rate=tr, count_max=tm)
write(*,*) ’count
=t0=’, t0; write(*,*) ’count_rate=tr=’, tr write(*,*) ’count_max =tm=’, tm;
n=1000000000; m=2000000000
call system_clock(count=t1); do i=1,n; enddo; call system_clock(count=t2)
do i=1,n; enddo; call system_clock(count=t3)
call system_clock(count=t4); do i=1,m; enddo; call system_clock(count=t5)
write(*,*) ’ введи число ’; read(*,*) i; call system_clock(t6)
write(*,1000)
write(*,1001) t1, t2, dble(t2-t1)/tr; write(*,1002) t2, t3, dble(t3-t2)/tr write(*,1003) t4, t5, dble(t5-t4)/tr; write(*,1004) t0, t6, dble(t6-t0)/tr
1000 format(1x,15x,’
Момент
Момент
Время ’/&
&
1x,15x,’
запуска завершения исполнения’)
1001 format(1x,’ Первого цикла
:’, i7, 5x, i7, 7x, f7.3)
1002 format(1x,’ Второго цикла :’, i7, 5x, i7, 7x, f7.3)
1003 format(1x,’ Третьего цикла :’, i7, 5x, i7, 7x, f7.3)
1004 format(1x,’ Всего расчета
:’, i7, 5x, i7, 7x, f7.3)
end program tsttime7
$ gfortran tsttime7.f95
! Наглядно видно, ФОРТРАН-подпрограмма SYSTEM-CLOCK
$ time ./a.out
! отмеряет отсчеты таймера реального времени:
count
=t0=
0 !
46850/1000 = 46.85сек (real),
count_rate=tr=
1000 ! тогда как С-функция clock() измеряет отсчеты count_max =tm=
2147483647 ! времени работы процессора введи число
!...........................
777
!
Момент
Момент
Время
!
запуска завершения исполнения !
Первого цикла
:
0 5300 5.300
!
Второго цикла :
5300 9732 5.270
! real
0m46.854s
Третьего цикла :
9732 20352 10.620
! user
0m20.326s
Всего расчета
:
0 46850 46.850
! sys
0m0.007s
367

12.6
GFORTRAN-подпрограмма ETIME.
Помимо встроенных процедур оценки временных затрат, упомянутых выше, компилятор gfortran имеет еще подпрограмму etime(artime,
result). Подпрограмма удобна для замера процессорного времени ис- полнения исследуемых фрагментов программ. Подпрограмма имеет два аргумента типа real, причем по структуре: первый – вектор из двух эле- ментов, а второй – скалярная переменная, значение которой равно сумме значение элементов вектора. В первый элемент вектора подпрограмма помещает процессорное время, израсходованное командами пользовате- ля с момента запуска исполнимого кода (т.е. то самое время, которому утилита time сопоставляет имя user). Во второй элемент вектора по- мещается время, затраченное системой. Второй аргумент хранит сумму времен user+sys.
program tsttime8; implicit none integer(8) :: i, n real, dimension(2) :: artime; real :: res0, res1, dt; real(8) s, di, z call etime(artime, res0)
write(*,’(a,f7.3,a,f7.3,a,f7.3)’) ’
artime(1)=’, artime(1),&

artime(2)=’, artime(2),&

res0=’, res0
n=1000000000; z=dble(n)
s=0; do i=1,n; di=dble(i); s=s+di*di; enddo
!
Имитация долгого счета.
call etime(artime, res1);
dt=res1-res0
write(*,’(a,f7.3,a,f7.3,a,f7.3)’) ’
artime(1)=’, artime(1),&

artime(2)=’, artime(2),&

res1=’, res1
write(*,’(42x,a,f7.3)’) ’
dt=’, dt write(*,*) ’ s=’,s write(*,*) ’ s=’,z*(z+1)*(2*z+1)/6
end program tsttime8
$ gfortran tsttime8.f95 -O0
$ time ./a.out artime(1)=
0.000
artime(2)=
0.002
res0=
0.002
artime(1)=
6.488
artime(2)=
0.003
res1=
6.491
dt=
6.488
s=
3.3333333383335519E+026
s=
3.3333333383333334E+026
real
0m6.492s user
0m6.488s sys
0m0.003s
368


12.7
Чуть-чуть о профилировании.
Указанные выше способы замера времени обладают рядом существенных недостатков, хотя на первый взгляд, достаточно удобны при исследова- нии небольших программ.
1. Они требуют вкрапления в текст программы операторов вызова вре- менн ´
ых процедур и замеров, что неудобно, так как после уяснения всех вопросов потребуется удаление этих вызовов, не говоря уже об исправлении опечаток и поиском ошибок, допускаемых при этом.
1   ...   15   16   17   18   19   20   21   22   23

2. В случае сложных проектов при наличии большого числа вложен- ных вызовов упомянутые встроенные временн ´
ые функции вряд ли позволят оперативно обнаружить причину происходящего. Для ре- шения подобных проблем существуют специальные утилиты, назы- ваемые профилировщиками (иногда профайлерами).
3. Профилировщик позволяет без внесения каких-либо изменений в исходные тексты проекта получить временн ´
ые затраты различных частей программы и сообщить о них программисту, не смешивая временн´
ую статистику с результатами работы программы.
Рассмотрим чуть изменённый и переписанный на ФОРТРАНе исходный текст СИ-программы из https://www.ibm.com/developerworks/ru/library/l-gnuprof).
function a() result(g)
! Функция a() моделирует долгоработающий implicit none
!
алгоритм integer i, g i=0; g=0
do while (i<100000)
g=g+i; i=i+1
enddo end function a function b() result(g)
! Функция b() моделирует алгоритм, работающий implicit none
! в четыре раза дольше алгоритма a().
integer i, g i=0; g=0
do while (i<400000)
g=g+i; i=i+1
enddo end function b
369
program main; implicit none
! Главная программа interface integer function a() result(g); end function a integer function b() result(g); end function b end interface integer i, N, p, q write(*,*) ’введи N (число итераций)’
! вводит N - количество read(*,*) N
! вызовов процедур write(*,*) ’N=’, N
!
a() и (b)
if (N<1) then; write(*,*) ’Нет итераций!’
! и stop 1
!
в случае N>=1
else
!
осуществляет их i=N;
!
вызов в теле do while (i/=0)
!
цикла с предусловием.
p=a();
!
q=b();
! В тексте программы НЕТ
i=i-1
! вызова встроенных процедур,
enddo
! нацеленных на замеры длин endif write(*,*) ’p=’,p
! промежутков времени.
write(*,*) ’q=’,q
! промежутков времени.
stop 0
end program main
Для вызова gnu-профилировщика gprof необходимо:
1) провести компиляцию программы при включённой опции -pg ком- пилятора gfortran: gfortran -pg example1.f90 2) осуществить активацию исполнимого файла:
./a.out
# ./a.out, получая в качестве введи N (число итераций)
# результата (наряду с выводом,
10000
# запланированным программой,
N=
10000
# и дополнительный файл gmon.out p=
704982704
# в чём можно убедиться посредством q= -1604578624
# команды ls.
STOP 0
# В gmon.out внесена вся информация ls
# о временн’ых затратах, которую a.out example1.f90
gmon.out # можно извлечь, запустив gprof.
3) осуществить запуск профилировщика:
gprof ./a.out gmon.out -p > flat.res.
370

В последней команде
• ./a.out – исполнимый файл;
• gmon.out – соответствующий информационный файл для gprof
• -p – опция gprof, информирующая утилиту о том, что из всей ин- формации мы хотим видеть лишь простой профиль.
• flat.res – имя файла, в который будет помещён простой профиль flat.res содержит таблицу с некоторой статистикой временн ´
ых замеров:
Flat profile:
Each sample counts as 0.01 seconds.
%
cumulative self self total time seconds seconds calls s/call s/call name
80.96 12.12 12.12 10000 0.00 0.00
b_
19.62 15.06 2.94 10000 0.00 0.00
a_
0.00 15.06 0.00 1
0.00 15.06
MAIN__
В частности, можно видеть, что процедура b(), действительно, работает вчетверо медленнее нежели процедура a (12.12/2.94 ∼ 4), так что 80%
всего времени работы программы занимает процедура b() и только 20%
процедура a().
Смысловая нагрузка столбцов таблицы простого профиля.
%
(процент времени) – Отношение временн ´
ых затрат time функции, имя которой находится в самом правом столбце простого профиля, ко всему времени работы исполнимого кода (в процентах);
cumulative (накапливающиеся секунды) – общее время secons
(в секундах), затраченное компьютером на работу данной функции, c добавлением времени, которое затрачено функциями, имена которых (в самом правом столбце) расположены выше имени данной;
self
(собственные секунды) – время (в секундах),
seconds затраченных только данной функцией. Именно по нему и упорядочиваются (в порядке убывания времени) строки таблицы простого профиля
371