Почему при параллельных вычислениях время выполнения всех потоков (4) больше, чем при использовании только половины (2)?

#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% использования каждого ядра. В этом случае использование меньшего количества потоков, чем количество доступных ядер, не имеет смысла.