Performance 如何优化函数并最小化分配
以下函数生成的素数最多为Performance 如何优化函数并最小化分配,performance,fortran,dynamic-memory-allocation,Performance,Fortran,Dynamic Memory Allocation,以下函数生成的素数最多为N。对于较大的N,这会变得非常缓慢,对于N=10**7,我的Julia实现速度快了5倍。我想创建一个大整数数组并使用pack收集结果是最慢的部分。我尝试先计算.true.s,然后分配res(:)并使用循环填充它,但在这种情况下,当我迭代prims数组两次时,加速比可以忽略不计(4%)。在《朱莉娅》中,我使用了findall,这正是我所做的;迭代数组两次,首先计算trues并分配结果,然后填充它。有什么想法吗?多谢各位 编译器: 英特尔(R)Visual Fortran英特
N
。对于较大的N
,这会变得非常缓慢,对于N=10**7
,我的Julia实现速度快了5倍。我想创建一个大整数数组
并使用pack
收集结果是最慢的部分。我尝试先计算.true.
s,然后分配res(:)
并使用循环填充它,但在这种情况下,当我迭代prims
数组两次时,加速比可以忽略不计(4%)。在《朱莉娅》中,我使用了findall
,这正是我所做的;迭代数组两次,首先计算trues并分配结果,然后填充它。有什么想法吗?多谢各位
编译器:
英特尔(R)Visual Fortran英特尔(R)64编译器,适用于在英特尔(R)64上运行的应用程序,版本18.0.3.210构建20180410(在Windows 10上)
选项:ifort-warn/O3-堆数组:8000000
program main
implicit none
integer, allocatable :: primes(:)
integer :: t0, t1, count_rate, count_max
call system_clock(t0, count_rate, count_max)
primes = do_primes(10**7)
call system_clock(t1)
print '(a,f7.5,a)', 'Elapsed time: ', real(t1-t0)/count_rate, ' seconds'
print *, primes(1:10)
contains
function do_primes(N) result (res)
integer, allocatable :: res(:), array(:)
logical, allocatable :: prims(:)
integer :: N, i, j
allocate (prims(N))
prims = .true.
i = 3
do while (i * i < N)
j = i
do while (j * i < N)
prims(j*i) = .false.
j = j + 2
end do
i = i + 2
end do
prims(1) = .false.
prims(2) = .true.
do i = 4, N, 2
prims(i) = .false.
end do
allocate (array(N))
do i = 1, N
array(i) = i
end do
res = pack(array, prims)
end
end
实施过程:
function do_primes(N)
prims = trues(N)
i = 3
while i * i < N
j = i
while j * i < N
prims[j*i] = false
j = j + 2
end
i = i + 2
end
prims[1] = false
prims[2] = true
prims[4:2:N] .= false
return findall(prims)
end
当您说“我想创建一个大整数数组并使用pack收集结果是最慢的部分”时,您是否实际分析了代码?我原以为嵌套循环是花费时间最多的部分。为什么只做while而不是直接的do循环?我的理解是后者提供了更多的优化机会。只是我看到嵌套循环在算法中是必不可少的,无法避免,所以对我来说最明显的事情就是避免不必要地创建一个大数组来打包结果。
do while
对于内环是方便的,因为终止条件是j*ii如果嵌套环占用所有的时间,则通过优化组件无法获得速度提升。请使用分析器运行代码,并告诉我们哪些部分需要时间。然后,也只有到那时,你才知道该把精力集中在哪里。我将从正确实施埃拉托斯琴斯筛算法开始,你做了太多茱莉亚似乎用在prims上的不必要的工作,这可能有助于提高速度(只是猜测…)。如果我将prims=trues(N)改为prims=fill(true,N),那么在我的mac电脑上,代码的速度会慢约2倍(这接近于我用修改过的fortran代码所能得到的速度)。FWIW,我在Python+中尝试了类似的代码,然后经过的时间大约是Julia的x3(带位数组)和gfortran-10的x1.5。此外,Julia和Numba使用LLVM,因此差异可能与LLVM与GCC有关(只是猜测…)当您说“我猜创建一个大整数数组并使用pack收集结果是最慢的部分”时,您是否实际分析了代码?我原以为嵌套循环是花费时间最多的部分。为什么只做while而不是直接的do循环?我的理解是后者提供了更多的优化机会。只是我看到嵌套循环在算法中是必不可少的,无法避免,所以对我来说最明显的事情就是避免不必要地创建一个大数组来打包结果。do while
对于内环是方便的,因为终止条件是j*ii如果嵌套环占用所有的时间,则通过优化组件无法获得速度提升。请使用分析器运行代码,并告诉我们哪些部分需要时间。然后,也只有到那时,你才知道该把精力集中在哪里。我将从正确实施埃拉托斯琴斯筛算法开始,你做了太多茱莉亚似乎用在prims上的不必要的工作,这可能有助于提高速度(只是猜测…)。如果我将prims=trues(N)改为prims=fill(true,N),那么在我的mac电脑上,代码的速度会慢约2倍(这接近于我用修改过的fortran代码所能得到的速度)。FWIW,我在Python+中尝试了类似的代码,然后经过的时间大约是Julia的x3(带位数组)和gfortran-10的x1.5。此外,Julia和Numba使用LLVM,因此差异可能与LLVM与GCC有关(只是猜测…)
function do_primes(N)
prims = trues(N)
i = 3
while i * i < N
j = i
while j * i < N
prims[j*i] = false
j = j + 2
end
i = i + 2
end
prims[1] = false
prims[2] = true
prims[4:2:N] .= false
return findall(prims)
end
using Benchmarktools
@benchmark do_primes(10^7)
BenchmarkTools.Trial:
memory estimate: 6.26 MiB
allocs estimate: 5
--------------
minimum time: 32.227 ms (0.00% GC)
median time: 32.793 ms (0.00% GC)
mean time: 34.098 ms (3.92% GC)
maximum time: 94.479 ms (65.46% GC)
--------------
samples: 147
evals/sample: 1