#parallel-processing #fortran #openmp #gfortran
#параллельная обработка #fortran #openmp #gfortran
Вопрос:
Например, я использую этот код (процессор: 4 ядра (поток на ядро)):
program main
use omp_lib
implicit none
integer, parameter:: ma=100, n=10000, mb= 100
integer:: istart, iend
real, dimension (ma,n) :: a
real, dimension (n,mb) :: b
real, dimension (ma,mb) :: c = 0.
integer:: i,j,k, threads=2, ppt, thread_num
integer:: toc, tic, rate
real:: time_parallel, time
call random_number (a)
call random_number (b)
!/////////////////////// 1- PARALLEL PRIVATE ///////////////////////
CALL system_clock(count_rate=rate)
call system_clock(tic)
ppt = ma/threads
!$ call omp_set_num_threads(threads)
!$omp parallel default(shared) private(istart, iend, amp;
!$omp thread_num, i)
!$ thread_num = omp_get_thread_num()
!$ istart = thread_num*ppt 1
!$ iend = min(ma, thread_num*ppt ppt)
do i= istart,iend
do j= 1,mb
do k= 1,n
c(i,j) = c(i,j) a(i,k)*b(k,j)
end do
end do
end do
!$omp end parallel
print*, 'Result in parallel mode'
!$ print*, c(85:90,40)
call system_clock(toc)
time_parallel = real(toc-tic)/real(rate)
!/////////////////////// 2-normal execution ///////////////////////
c = 0
CALL system_clock(count_rate=rate)
call system_clock(tic)
call system_clock(tic)
do i= 1,ma
do j= 1,mb
do k= 1,n
c(i,j) = c(i,j) a(i,k)*b(k,j)
end do
end do
end do
call system_clock(toc)
time = real(toc-tic)/real(rate)
print*, 'Result in serial mode'
print*, c(85:90,40)
print*, '------------------------------------------------'
print*, 'Threads: ', threads, '| Time Parallel Private', time_parallel, 's '
print*, ' Time Normal ', time, 's'
!----------------------------------------------------------------
end program main
Я получаю следующие результаты:
Первое выполнение:
Result in parallel mode
2477.89478 2528.50391 2511.84204 2528.12061 2500.79517
2510.69971
Result in serial mode
2477.89478 2528.50391 2511.84204 2528.12061 2500.79517
2510.69971
Threads: 2 | Time Parallel Private 0.379999995 s
Time Normal 0.603999972 s
Второе выполнение:
Result in parallel mode
2492.20679 2496.56152 2500.58203 2516.51685 2516.43604
2530.71313
Result in serial mode
2492.20679 2496.56152 2500.58203 2516.51685 2516.43604
2530.71313
------------------------------------------------
Threads: 4 | Time Parallel Private 1.11500001 s
Time Normal 0.486000001 s
Он был скомпилирован с использованием:
gfortran -Wall -fopenmp -g -O2 -o prog.exe prueba.f90
./prog.exe
Комментарии:
1. Вы действительно уверены, что у вас действительно 4 ядра. Или это какая-то гиперпоточность? Какую модель процессора вы используете? Разница в «нормальном» времени выполнения вызывает подозрение. Также убедитесь, что у процессора достаточно времени для ускорения.
2. @VladimirF Модель — AMD A6-6310.
3. Я предполагаю, что все зависит от пропускной способности памяти. Вы не получите большей производительности без серьезной перезаписи с использованием тайлинга циклов или подобных трюков. Эффективное умножение матриц сложно и на самом деле не является задачей для новичка.
4. @Isaac Я также считаю ограничение пропускной способности памяти, но не лучше ли переставить циклы в порядок j -> k -> i (таким образом, распараллеливая j), а не текущий i -> j -> k? (хотя это может зависеть от формы массива …) первое дало мне немного лучшую производительность на core-i7 (4-ядерный, 2012, старый …). Также такие опции, как gfortran-10 -O2 -pipe -march=native -mtune=native -funroll-loops —param max-unroll-times= 4 -ftree-vectorize -Wall, могут дать некоторое ускорение (о чем я узнал от Стива Каргла в сети)
5. На моем компьютере с 6 ядрами / 12 потоками я получил от 0,16 до 0,18 секунды в последовательном режиме и 0,10 с с 2 потоками, 0,06 секунды с 4 потоками и 0,04 секунды с 12 потоками. Независимо от того, является ли распараллеливание оптимальным или нет, оно что-то делает и дает полезное ускорение. Я не наблюдал, чтобы 4 потока были хуже 2.
Ответ №1:
Если у вас N ядер и используется N потоков, некоторые из ваших потоков переключаются на какой-то другой процесс и потоки. Поэтому предпочтительнее использовать меньше потоков, чем доступных ядер.
Комментарии:
1. Часто для этого одного вычисления используется весь компьютер. ОС и связанные с ней службы по-прежнему выполняют свою работу, но это довольно незначительно.
2. Это зависит от контекста. В контексте высокопроизводительных вычислений, когда у вас обычно есть целый компьютер (или узел на суперкомпьютере), доступный для вашего процесса, тогда с помощью well-optimized вы можете регулярно достигать> 99% использования каждого ядра. В этом случае использование меньшего количества потоков, чем количество доступных ядер, не имеет смысла.