我尝试在julia命令中运行以下函数,但是在对函数进行计时时,我看到了太多的内存分配,我无法弄清楚为什么。
julia> @time pdpf(1000, 10000)
17.621551 seconds (9.00 M allocations: 30.294 GB, 7.10% gc time)
在我的笔记本电脑上的julia命令中运行该功能,我得到以下令人震惊的数字:
<div id="MAIN">
<div id="ColumnOne">
<table style="width: 100%">
<tr>
<td>111 <br/>
111</td>
</tr>
</table>
</div>
<div id="ColumnTwo">
<table style="width: 100%">
<tr>
<td>222</td>
</tr>
</table>
</div>
<div id="ColumnThree">
<table style="width: 100%">
<tr>
<td>333 333 <br> 333 <br> 333 </td>
</tr>
</table>
</div>
<div id="ColumnFour">
<table style="width: 100%">
<tr>
<td>444 444 <br> 444 444 <br> 444 444 <br> 444 </td>
</tr>
</table>
</div>
我的代码出了什么问题?任何帮助表示赞赏。
答案 0 :(得分:7)
我不认为这种内存分配是如此令人惊讶。例如,考虑内循环执行的所有时间:
for m = 1:length(Pf)
这会给你100次执行
for k = 1:iters
这会根据您提供给函数的参数为您提供10,000次执行。
randn(L)
这会根据您提供给函数的参数为您提供长度为1,000的随机向量。
因此,考虑到这些,您已经生成了100 * 10,000 * 1000 = 10亿个Float64随机数。它们中的每一个都需要64位= 8个字节。即8GB就在那里。而且,您有两次拨打randn(L)
,这意味着您已经拨打了16GB的分配费用。
然后你有y = s + n
这意味着另外8GB的分配,最多可以达到24GB。我还没有仔细查看剩下的代码,以便从24GB到30GB的分配,但这应该表明GB分配开始在代码中添加并不难。
如果你正在寻找需要改进的地方,我会给你一个暗示,通过使用普通随机变量的属性可以改善这些行:
n = randn(L)
s = sqrt(snr) * randn(L)
y = s + n
您应该可以通过这种方式轻松地将此处的分配从24GB减少到8GB。请注意y
将是一个正常的随机变量,因为您已经定义了它,并想出一种方法来生成一个与y
现在具有相同分布的正态随机变量。
另一个小问题,snr
是函数内的常量。然而,你不断分开sqrt
100万次。在某些情况下,检查您的工作&#39;可能会有所帮助,但我认为您可以确信计算机会在第一时间正确运行,因此您不需要让它继续重新进行此计算; )。还有其他类似的地方可以改进您的代码,以避免重复计算,我将留给您找到。
答案 1 :(得分:2)
aireties为您分配如此多的分配提供了一个很好的答案。您可以采取更多措施来减少分配数量。使用this property我们知道y = s+n
确实是y = sqrt(snr) * randn(L) + randn(L)
,因此我们可以改为y = rvvar*randn(L)
,其中rvvar= sqrt(1+sqrt(snr)^2)
在循环外定义(感谢您的修复!) 。这将使所需的随机变量数减半。
在循环之外,您可以保存sqrt(2/L)
以缩短一点时间。
我不认为转置是特殊的,所以请尝试使用dot(y,y)
代替y'*y
。我知道dot
肯定只是一个循环而不必转置,而另一个可能会根据Julia的版本进行转置。
有助于性能(但不是分配)的东西就是使用一个大的randn(L,iters)并循环通过它。原因是因为如果你把所有的随机数一次全部制作它会更快,因为它可以使用SIMD和其他一些好东西。如果您想在不更改代码的情况下隐式执行此操作,可以使用ChunkedArrays.jl在rands = ChunkedArray(randn,L)
初始化它,然后每次需要randn(L)
时使用 1}}。在ChunkedArray中,它实际上会生成更大的向量并根据需要补充它们,但是像这样你可以得到你的next(rands)
,而不必跟踪所有这些。
编辑:
ChunkedArrays可能只在L较小时节省时间。这给出了代码:
randn(L)
使用两个randn调用运行一半的时间。事实上,从ProfileViewer我们得到:
function pdpf(L::Int64, iters::Int64)
snr_dB = -10
snr = 10^(snr_dB/10)
Pf = 0.01:0.01:1
thresh = rand(100)
Pd = rand(100)
rvvar= sqrt(1+sqrt(snr)^2)
for m = 1:length(Pf)
i = 0
for k = 1:iters
y = rvvar*randn(L)
energy_fin = (y'*y) / L
@inbounds thresh[m] = erfcinv(2Pf[m]) * sqrt(2/L) + 1
if energy_fin[1] >= thresh[m]
i += 1
end
end
@inbounds Pd[m] = i/iters
end
end
我圈出了@profile pdpf(1000, 10000)
using ProfileView
ProfileView.view()
行的两个部分,所以绝大部分时间都是随机数生成。上次我检查过你可以通过更改为from the Google Summer of Code page来获得随机数生成的体面加速,但是你需要将MKL链接到你的Julia版本。请注意RNG.jl您可以看到有一个项目可以使用更快的psudo-rng进行回购。它看起来已经实现了一些新的。您可能想要检查它们,看看它们是否提供加速(或帮助完成该项目!)