Julia函数中的内存分配

JHZ*_*JHZ 6 memory function julia

这是Julia 0.5中的一个简单函数.

function foo{T<:AbstractFloat}(x::T)
  a = zero(T)
  b = zero(T)
  return x
end
Run Code Online (Sandbox Code Playgroud)

我开始了julia --track-allocation=user.然后include("test.jl").test.jl只有这个功能.跑foo(5.).然后Profile.clear_malloc_data().foo(5.)再次在REPL中.退出朱莉娅.查看文件test.jl.mem.

        - function foo{T<:AbstractFloat}(x::T)
        -   a = zero(T)
   194973   b = zero(T)
        0   return x
        - end
        - 
Run Code Online (Sandbox Code Playgroud)

为什么这里分配了194973字节的内存?这也不是该功能的第一行.虽然之后Profile.clear_malloc_data(),这应该不重要.

Fen*_*ang 8

让我们澄清相关文档的某些部分,这可能有点误导:

在解释结果时,有一些重要的细节.在用户设置下,由REPL代码本身发生的事件,直接从REPL调用的任何函数的第一行将显示分配.

实际上,分配线不是第一线.然而,它仍然是第一个跟踪线,因为Julia 0.5在实际的第一个语句上有跟踪分配的一些问题(这已在v0.6上修复).请注意,它也可能(与文档所说的相反)传播到函数中,即使它们被注释@noinline.唯一真正的解决方案是确保首先声明所调用的内容是您不想测量的内容.

更重要的是,JIT编译还增加了分配计数,因为Julia的编译器大部分是用Julia编写的(并且编译通常需要内存分配).建议的过程是通过执行要分析的所有命令强制编译,然后调用Profile.clear_malloc_data()来重置所有分配计数器.最后,执行所需的命令并退出Julia以触发生成.mem文件.

你是对的,Profile.clear_malloc_data()可以防止计算JIT编译的分配.但是,本款与第一款分开; clear_malloc_data由于"REPL代码本身发生的事件",因此对分配没有任何作用.

实际上,正如我确定您所怀疑的那样,此函数中没有分配:

julia> function foo{T<:AbstractFloat}(x::T)
         a = zero(T)
         b = zero(T)
         return x
       end
foo (generic function with 1 method)

julia> @allocated foo(5.)
0
Run Code Online (Sandbox Code Playgroud)

您看到的数字是由REPL本身的事件引起的.要避免此问题,请将代码包装到函数中进行测量.也就是说,我们或许可以禁用内联后用这个作为我们的测试工具,foo@noinline.例如,这是一个修订版test.jl:

@noinline function foo{T<:AbstractFloat}(x::T)
    a = zero(T)
    b = zero(T)
    return x
end

function do_measurements()
    x = 0.  # dummy statement
    x += foo(5.)
    x       # prevent foo call being optimized out
            # (it won't, but this is good practice)
end
Run Code Online (Sandbox Code Playgroud)

然后是REPL会话:

julia> include("test.jl")
do_measurements (generic function with 1 method)

julia> do_measurements()
5.0

julia> Profile.clear_malloc_data()

julia> do_measurements()
5.0
Run Code Online (Sandbox Code Playgroud)

这产生了预期的结果:

        - @noinline function foo{T<:AbstractFloat}(x::T)
        0     a = zero(T)
        0     b = zero(T)
        0     return x
        - end
        -
        - function do_measurements()
   155351     x = 0.  # dummy statement
        0     x += foo(5.)
        0     x       # prevent foo call being optimized out
        -             # (it won't, but this is good practice)
        - end
        -
Run Code Online (Sandbox Code Playgroud)