朱莉娅 - 有没有办法让这个就地分配功能看起来更好

时间:2017-09-23 23:13:52

标签: julia allocation

请考虑以下代码摘录:

q = zeros(5,5);
x = rand(5,5);
function testFunc!(n, q, x)
    for i = 1:n
        q .= x.^2;
    end
end

我初始化q并通过就地分配功能更新q中的值。无论我如何选择n,分配的数量都是不变的,因为我没有创建任何新内容。

但是,有没有办法编写一个名为testFunc2的函数,如下所示:

q = zeros(5,5)
x = rand(5,5);
q = testFunc2(n, x)

这样内存分配对n不变。

我知道这看起来很傻,但在我的实际代码中,我有许多类似于q的变量,并且我有一个循环,每次迭代都会更新它们。我不希望代码占用过多的内存。如果我编写一个看起来像testFunc2(n, x, q1, q2, q3, q4)的函数,就语法而言看起来会非常麻烦。因此,我希望能够q1, q2, q3, q4 = testFunc2(n, x)而不必担心记忆。

2 个答案:

答案 0 :(得分:3)

如果我理解正确,最简单的方法是使用闭包:

let 
   q1 = similar(q)
   q2 = similar(q)
   global function testFunc2(n, x)
       for i = 1:n
          q1 .= x.^2
          q2 .= x.^2
       end
       return q1, q2
   end
end

julia> @btime testFunc2(10, $x)
  786.000 ns (0 allocations: 0 bytes)

julia> @btime testFunc2(100, $x)
  7.476 μs (0 allocations: 0 bytes)

正如@Liso在下面的评论中指出的那样,如果我们不尝试在呼叫之间共享相同的内存,则应该返回副本,例如: return copy(q1), copy(q2)而不是return q1, q2,我们仍然可以获得恒定的内存分配:

@btime testFunc2(10, $x)
  821.684 ns (3 allocations: 704 bytes)

@btime testFunc2(100, $x)
  7.326 μs (3 allocations: 704 bytes)

无论如何,选择哪种方法取决于相应的用例,functor是一种更灵活的选择。

答案 1 :(得分:2)

我不确定q1, q2, q3, q4 = testFunc2(n, x)看起来是否比testFunc2!(n, x, q1, q2, q3, q4)好多了,但您可以使用这样的宏进行这种类型的转换:

macro wrap(ex)
    outputs = ex.args[1].args
    ex.args[2].args[1] = Symbol(ex.args[2].args[1], "!")
    append!(ex.args[2].args, outputs)
    esc(ex)
end

扩展

@wrap q1, q2, q3, q4 = testFunc2(n, x)

(q1, q2, q3, q4) = testFunc2!(n, x, q1, q2, q3, q4)