[英]Python Pandas: Why is numpy so much faster than Pandas for column assignment? Can I optimize further?
[英]How can numpy be so much faster than my Fortran routine?
我得到一個512 ^ 3數組,表示模擬的溫度分布(用Fortran編寫)。 該數組存儲在大小約為1 / 2G的二進制文件中。 我需要知道這個數組的最小值,最大值和平均值,因為我很快就需要了解Fortran代碼,我決定試一試,並提出了以下非常簡單的例程。
integer gridsize,unit,j
real mini,maxi
double precision mean
gridsize=512
unit=40
open(unit=unit,file='T.out',status='old',access='stream',&
form='unformatted',action='read')
read(unit=unit) tmp
mini=tmp
maxi=tmp
mean=tmp
do j=2,gridsize**3
read(unit=unit) tmp
if(tmp>maxi)then
maxi=tmp
elseif(tmp<mini)then
mini=tmp
end if
mean=mean+tmp
end do
mean=mean/gridsize**3
close(unit=unit)
在我使用的機器上,每個文件大約需要25秒。 這讓我覺得很長,所以我繼續在Python中做了以下事情:
import numpy
mmap=numpy.memmap('T.out',dtype='float32',mode='r',offset=4,\
shape=(512,512,512),order='F')
mini=numpy.amin(mmap)
maxi=numpy.amax(mmap)
mean=numpy.mean(mmap)
現在,我預計這會更快,但我真的被吹走了。 在相同條件下只需不到一秒鍾。 平均值偏離我的Fortran例程找到的那個(我也使用128位浮點運行,所以我不知何故更多地信任它),但僅限於第7位有效數字左右。
numpy怎么這么快? 我的意思是你必須查看數組的每個條目才能找到這些值,對吧? 我在Fortran例行程中做了一件非常愚蠢的事情,因為它花了這么長時間嗎?
編輯:
要回答評論中的問題:
iso_fortran_env
,它提供了128位浮點數。 編輯2:
我實現了@Alexander Vogt和@casey在他們的答案中提出的建議,它和numpy
一樣快,但現在我有一個精確的問題,因為@Luaan指出我可能會得到。 使用32位浮點數組,由sum
計算的平均值為20%。 干
...
real,allocatable :: tmp (:,:,:)
double precision,allocatable :: tmp2(:,:,:)
...
tmp2=tmp
mean=sum(tmp2)/size(tmp)
...
解決了這個問題但增加了計算時間(不是很多,但顯着)。 有沒有更好的方法來解決這個問題? 我找不到從文件中直接讀單打的方法。 numpy
如何避免這種情況?
感謝目前為止所有的幫助。
您的Fortran實現存在兩個主要缺點:
此實現確實執行與您相同的操作,並且在我的機器上運行速度提高了20倍:
program test
integer gridsize,unit
real mini,maxi,mean
real, allocatable :: tmp (:,:,:)
gridsize=512
unit=40
allocate( tmp(gridsize, gridsize, gridsize))
open(unit=unit,file='T.out',status='old',access='stream',&
form='unformatted',action='read')
read(unit=unit) tmp
close(unit=unit)
mini = minval(tmp)
maxi = maxval(tmp)
mean = sum(tmp)/gridsize**3
print *, mini, maxi, mean
end program
我們的想法是將整個文件一次性讀入一個數組tmp
。 然后,我可以直接在數組上使用MAXVAL
, MINVAL
和SUM
函數。
對於准確性問題:只需使用雙精度值並即時進行轉換
mean = sum(real(tmp, kind=kind(1.d0)))/real(gridsize**3, kind=kind(1.d0))
只是略微增加了計算時間。 我嘗試在切片中執行元素操作,但這只會增加默認優化級別所需的時間。
在-O3
,元素加法比陣列操作好大約3%。 在我的機器上,雙精度和單精度操作之間的差異小於2% - 平均而言(個別運行偏差更多)。
這是使用LAPACK的非常快速的實現:
program test
integer gridsize,unit, i, j
real mini,maxi
integer :: t1, t2, rate
real, allocatable :: tmp (:,:,:)
real, allocatable :: work(:)
! double precision :: mean
real :: mean
real :: slange
call system_clock(count_rate=rate)
call system_clock(t1)
gridsize=512
unit=40
allocate( tmp(gridsize, gridsize, gridsize), work(gridsize))
open(unit=unit,file='T.out',status='old',access='stream',&
form='unformatted',action='read')
read(unit=unit) tmp
close(unit=unit)
mini = minval(tmp)
maxi = maxval(tmp)
! mean = sum(tmp)/gridsize**3
! mean = sum(real(tmp, kind=kind(1.d0)))/real(gridsize**3, kind=kind(1.d0))
mean = 0.d0
do j=1,gridsize
do i=1,gridsize
mean = mean + slange('1', gridsize, 1, tmp(:,i,j),gridsize, work)
enddo !i
enddo !j
mean = mean / gridsize**3
print *, mini, maxi, mean
call system_clock(t2)
print *,real(t2-t1)/real(rate)
end program
這在矩陣列上使用單精度矩陣1范數SLANGE
。 運行時甚至比使用單精度數組函數的方法更快 - 並且沒有顯示精度問題。
numpy更快,因為你在python中編寫了更高效的代碼(並且大部分numpy后端是用優化的Fortran和C編寫的)和Fortran中非常低效的代碼。
看看你的python代碼。 您立即加載整個數組,然后調用可以在陣列上運行的函數。
看看你的fortran代碼。 您一次讀取一個值並使用它執行一些分支邏輯。
您的大部分差異是您在Fortran中編寫的碎片IO。
您可以像編寫python一樣編寫Fortran,你會發現它的運行速度要快得多。
program test
implicit none
integer :: gridsize, unit
real :: mini, maxi, mean
real, allocatable :: array(:,:,:)
gridsize=512
allocate(array(gridsize,gridsize,gridsize))
unit=40
open(unit=unit, file='T.out', status='old', access='stream',&
form='unformatted', action='read')
read(unit) array
maxi = maxval(array)
mini = minval(array)
mean = sum(array)/size(array)
close(unit)
end program test
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.