матричный продукт Fortran замедляется при вызове с помощью f2py через python - PullRequest
0 голосов
/ 05 декабря 2018

Я пытался использовать f2py для взаимодействия оптимизированного кода Fortran для векторного и матричного умножения с Python.Чтобы получить сравнение производительности, полезное для моих целей, я выполняю один и тот же продукт внутри цикла 100000 раз.С полным кодом Fortran продукт занимает 2,4 секунды (ifort), в то время как с F2py это занимает около 11 секунд.Просто для справки, с NumPy это занимает около 20 секунд.Я прошу и фортран, и часть Python записать разницу во времени до и после цикла, и с помощью f2py они пишут по 11 секунд, поэтому код не теряет время при передаче массивов.Я пытался понять, является ли это способом хранения массива numpy, но я не могу понять проблему.Есть ли у вас какие-либо идеи?Заранее спасибо

fortran Main

program Main
    implicit none
    save

    integer :: seed, i, j, k
    integer, parameter :: states =15
    integer, parameter :: tessere = 400
    real, dimension(tessere,states,states) :: matrix
    real, dimension(states) :: vector
    real :: start, finish
    real  :: prod(tessere)

    do i=1,tessere
       do j=1,states
          do k=1,states
              matrix(i,j,k) = i+j+k
          end do
       enddo
    end do
    do i=1,states
        vector(i) = i
    enddo
    call doubleSum(vector,vector,matrix,states,tessere,prod)

end program

подпрограмма fortran:

subroutine doubleSum(ket, bra, M , states, tessere,prod)
    integer :: its, j, k,t
    integer :: states
    integer :: tessere
    real, dimension(tessere,states,states) :: M
    real, dimension(states) :: ket
    real, dimension(states) :: bra
    real, dimension(tessere) :: prod
    real,dimension(tessere,states) :: ctmp

    call cpu_time(start)
    do t=1,100000
        ctmp=0.d0
        do k=1,states
             do j=1,states
                do its=1,tessere
                   ctmp(its,k)=ctmp(its,k)+ M(its,k,j)*ket(j)
                enddo
             enddo
        enddo
        do its=1,tessere
            prod(its)=dot_product(bra,ctmp(its,:))
        enddo
    enddo
    call cpu_time(finish)
    print '("Time = ",f6.3," seconds.")',finish-start
end subroutine

скрипт python

import numpy as np
import time
import cicloS


M= np.random.rand(400,15,15)
ket=np.random.rand(15)

#M=np.asfortranarray(M)
#ket=np.asfortranarray(ket)

import time


start=time.time()  
prod=cicloS.doublesum(ket,ket,M)
end=time.time()
print(end-start)

.pyf, созданный с помощью f2py иотредактировано

!    -*- f90 -*-
! Note: the context of this file is case sensitive.

python module cicloS 
    interface  
        subroutine doublesum(ket,bra,m,states,tessere,prod) 
            real dimension(states),intent(in) :: ket
            real dimension(states),depend(states),intent(in) :: bra
            real dimension(tessere,states,states),depend(states,states),intent(in) :: m
            integer, optional,check(len(ket)>=states),depend(ket) :: states=len(ket)
            integer, optional,check(shape(m,0)==tessere),depend(m) :: tessere=shape(m,0)
            real dimension(tessere),intent(out) :: prod
        end subroutine doublesum
    end interface
end python module cicloS

1 Ответ

0 голосов
/ 01 февраля 2019

ОП указал, что наблюдаемая разница во времени выполнения между автономными и скомпилированными версиями кода F2PY была вызвана использованием различных компиляторов и флагов компилятора.

Для получения согласованного результата и, следовательно,Чтобы ответить на вопрос, необходимо убедиться, что F2PY использует желаемый 1) компилятор и 2) флаги компилятора.

Часть 1: укажите, какой компилятор Fortran должен использоваться F2PY

Список компиляторов Fortran, доступных для F2PY в целевой системе, можно отобразить, выполнив, например, python -m numpy.f2py -c --help-fcompiler.В моей системе это выдает (усечено):

Fortran compilers found:
  --fcompiler=gnu95    GNU Fortran 95 compiler (7)
  --fcompiler=intelem  Intel Fortran Compiler for 64-bit apps (19.0.1.144)

Вы можете указать F2PY, какой из доступных компиляторов Fortran использовать, добавив соответствующий флаг --fcompiler к вашей команде компиляции.Для использования ifort например (при условии, что вы уже создали и отредактировали файл cicloS.pyf):

python -m numpy.f2py --fcompiler=intelem -c cicloS.pyf sub.f90

Часть 2. Укажите дополнительные флаги компилятора , которые будут использоваться F2PY

Обратите внимание, что выходные данные --help-fcompiler на предыдущем шаге также отображают флаги компилятора по умолчанию (см., Например, compiler_f90), которые F2PY определяет для каждого доступного компилятора.Снова в моей системе это было (усечено и упрощено для большинства соответствующих флагов):

  • gnu95: -O3 -funroll-loops
  • intelem: -O3 -xSSE4.2 -axCORE-AVX2,COMMON-AVX512

Вы можете указать предпочтительные флаги оптимизации для F2PY с флагом --opt в вашей команде компиляции (см. Также --f90flags в документации ), которая теперь становится, например:

python -m numpy.f2py --fcompiler=intelem --opt='-O1' -c cicloS.pyf sub.f90

Сравните время выполнения для автономной версии и версии F2PY:

Компиляция автономного исполняемого файла с ifort -O1 sub.f90 main.f90 -o main и скомпилированной версии F2PY из Часть 2 , я получаю следующий вывод:

./main
Time =  5.359 seconds.

python test.py
Time =  5.297 seconds.
5.316878795623779

Затем, компилируя автономный исполняемый файл с ifort -O3 sub.f90 main.f90 -o main и (по умолчанию) скомпилированной версией F2PY из Часть 1 , я получаю следующие результаты:

./main
Time =  1.297 seconds.

python test.py
Time =  1.219 seconds.
1.209657907485962

Таким образом, показываяаналогичные результаты для автономной версии и версии F2PY, а также влияние флагов компилятора.

Комментарий к временным массивам

Несмотря на то, что вы не наблюдаете замедление, обратите внимание, что F2PY принудительносделать временные копииRayys Mket) в вашем примере Python по двум причинам:

  • 3D-массив M, который вы передаете cicloS.doublesum(), является массивом NumPy по умолчанию с упорядочением C (строка-мажор).Поскольку Fortran использует упорядочение по главному столбцу, F2PY сделает копии массива.Закомментированный np.asfortranarray() должен исправить эту часть проблемы.
  • следующая причина для копий массива (также для ket) заключается в том, что существует несоответствие между реальными типами на Python (по умолчанию 64-битный,двойная точность с плавающей запятой) и Fortran (real дает точность по умолчанию, вероятно 32-битная с плавающей запятой) сторон вашего примераТаким образом, для этого снова создаются копии.

Вы можете получить уведомление, когда копии массива сделаны, добавив флаг -DF2PY_REPORT_ON_ARRAY_COPY=1 (также в документации ) к вашей компиляции F2PYкоманда.В вашем случае копий массивов можно полностью избежать, изменив dtype ваших M и ket матриц в Python (то есть M=np.asfortranarray(M, dtype=np.float32)) и ket=np.asfortranarray(ket, dtype=np.float32)), или, альтернативно, определив переменные real в вашем Fortranкод с соответствующим kind (например, добавьте use, intrinsic :: iso_fortran_env, only : real64 в свою подпрограмму и основную программу и определите действительные значения с помощью real(kind=real64).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...