我正在将一些代码从R移植到julia以熟悉该语言,我发现一些模式无法顺利翻译。考虑以下函数,
# Ricatti-Bessel and derivatives up to nmax, vectorised over x
function rb(x, nmax)
n = 1:nmax
nu = 0.5 + [0, n]
bj = hcat([besselj(nu, _x) for _x in x]...).'
# ^ first question ^
sq = repmat(sqrt(pi/2*x), 1, nmax+1)
bj .*= sq
xm = repmat(x, 1, nmax)
nm = repmat(n', length(x), 1)
# ^ second question ^
dpsi = bj[:,n] - nm .* bj[:,n+1] ./ xm
psi = bj[:,n+1]
return psi, dpsi # it'd be nice to return a "named list" instead
end
# rb(1:5,3)
第一个问题:这是使用besselj()
获取具有nmax列和长度(x)行的矩阵的最佳方法吗?在找到一个有效的模式之前,我不得不挠头一段时间。
第二个问题:我发现自己经常不得不在repmat内和/或外调换对象,是否可以指定输出大小和填充方向(行方式或逐方方式)?
也许我对整个事情采取了错误的方法:我习惯于使用矢量化函数(在R中,以及Matlab的旧记忆中),因为它们通常是线性代数快速例程的最短路径。 将x保持为标量是否更有意义,并且仅在最高级别循环?我担心通过这样做,我将无法利用BLAS等的快速矩阵/向量函数,并且基本上在julia中重写它们,更不用说明显的可读性损失了。 我应该强调我对最佳性能感兴趣,因为对于x的许多值,这个函数将被内部调用很多次。
答案 0 :(得分:5)
对于你的第一个问题,我将用以下矩阵理解来替换它:
nu = (0:nmax)+0.5
bj = [besselj(i,j) for j in x, i in nu]
对于你的第二个,我认为在Julia编写高性能代码的一个好原则是避免不必要的分配(当然,阅读performance tips!)Julia生成非常快速的指令 - 这就是为什么{ {1}}循环是完全正常的,除了线性代数(例如矩阵乘法)之外,对矢量化除了实际上并不重要。它做得不好的是避免分配不必要的内存(像for
这样的临时矩阵)。我用以下
sq
/ bj
行
sq
这很好,因为它只有一个分配,在我们之前(假设我们从Q1的答案开始):
nu = (0:nmax)+0.5
bj = [besselj(i,j)*sqrt(pi/2*j) for j in x, i in nu]
bj
sq
并将bj.*sq
重新绑定到此新内存(请注意,bj
不就地操作!)
您对“命名列表”的请求现在可能最好通过为此函数返回.*=
来实现(这根本不是一项昂贵的操作,并且在例如Julia的中很常见)矩阵分解代码,需要返回多个值)。或者,您可以返回type
,但这并不是惯用的。
对于Dict
行,我会给你两个选择。第一个是另一个矩阵理解:
dpsi
,另一个是loop-y:
dpsi = [ bj[i,j] - j * bj[i,j+1] / i
for i in 1:length(x), j in 1:nmax]
在这两种情况下,我都在避免分配临时工。同样,您的原始分配具有以下分配:
dpsi = zeros(length(x),nmax)
for i in 1:length(x), j in 1:nmax
dpsi[i,j] = bj[i,j] - j * bj[i,j+1] / i
end
xm
nm
(这将更改为0.4中的视图)bj[:,n]
(同上)bj[:,n+1]
nm .* bj[:,n+1]
我提出的两个版本只有一个分配,可能更接近问题的原始数学陈述
我的最终版本是
nm .* bj[:,n+1] ./ xm
我对function myrb(x, nmax)
bj = [ besselj(i,j)*sqrt(pi/2*j)
for j in x, i in (0:nmax)+0.5]
dpsi = [ bj[i,j] - j * bj[i,j+1] / i
for i in 1:length(x), j in 1:nmax]
psi = bj[:,2:nmax+1]
return psi, dpsi
end
一点都不太了解,但我猜它是整个事情中最慢的部分,所以所有这些在速度方面可能并不重要案件。对这个微观案例进行基准测试表明了这一点:
besselj
您可以使用分析器确认这一点(尽管我必须使用更大的输入:)
# original
elapsed time: 9.7578e-5 seconds (7176 bytes allocated)
elapsed time: 7.2644e-5 seconds (7176 bytes allocated)
elapsed time: 7.5709e-5 seconds (7176 bytes allocated)
# revised
elapsed time: 2.7536e-5 seconds (728 bytes allocated)
elapsed time: 2.7097e-5 seconds (728 bytes allocated)
elapsed time: 1.6601e-5 seconds (728 bytes allocated)
在我的机器上,该函数收集了429个样本,其中426个位于Julia内的@profile myrb(1:500,300)
Profile.print()
文件中,2个位于bessel.jl
,1个位于dpsi
。< / p>