运行julia功能的巨大内存分配?

时间:2016-07-15 15:07:27

标签: performance julia

我尝试在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>

我的代码出了什么问题?任何帮助表示赞赏。

2 个答案:

答案 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.jlrands = ChunkedArray(randn,L)初始化它,然后每次需要randn(L)时使用enter image description here 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

VSL.jl library

我圈出了@profile pdpf(1000, 10000) using ProfileView ProfileView.view() 行的两个部分,所以绝大部分时间都是随机数生成。上次我检查过你可以通过更改为from the Google Summer of Code page来获得随机数生成的体面加速,但是你需要将MKL链接到你的Julia版本。请注意RNG.jl您可以看到有一个项目可以使用更快的psudo-rng进行回购enter image description here。它看起来已经实现了一些新的。您可能想要检查它们,看看它们是否提供加速(或帮助完成该项目!)