与非可变结构相比,为什么当大量可变结构加载到内存中时,垃圾回收速度要慢得多?在这两种情况下,对象树应该具有相同的大小。
julia> struct Foo
a::Float64
b::Float64
c::Float64
end
julia> mutable struct Bar
a::Float64
b::Float64
c::Float64
end
julia> @time dat1 = [Foo(0.0, 0.0, 0.0) for i in 1:1e9];
9.706709 seconds (371.88 k allocations: 22.371 GiB, 0.14% gc time)
julia> @time GC.gc(true)
0.104186 seconds, 100.00% gc time
julia> @time GC.gc(true)
0.124675 seconds, 100.00% gc time
julia> @time dat2 = [Bar(0.0, 0.0, 0.0) for i in 1:1e9];
71.870870 seconds (1.00 G allocations: 37.256 GiB, 73.88% gc time)
julia> @time GC.gc(true)
47.695473 seconds, 100.00% gc time
julia> @time GC.gc(true)
41.809898 seconds, 100.00% gc time
Run Code Online (Sandbox Code Playgroud)
不可变结构可以直接存储在数组中。对于可变结构来说,这种情况永远不会发生。在您的情况下,Foo对象都直接存储在 中dat1,因此在创建 Arary 后实际上只有一个(尽管非常大)分配可访问。
在 的情况下dat2,每个Bar对象都会为其分配自己的一块内存,并且数组将包含对这些对象的引用。因此dat2,使用 ,您最终会得到 1G + 1 个可达分配。
您还可以使用以下命令查看此内容Base.sizeof:
julia> sizeof(dat1)
24000000000
julia> sizeof(dat2)
8000000000
Run Code Online (Sandbox Code Playgroud)
您会看到它dat1是原来的 3 倍,因为每个数组条目都Float64直接包含 3,而 中的条目dat2仅占用每个指针的空间。
附带说明:对于这些类型的测试,最好使用而BenchmarkTools.@btime不是内置的@time. 因为它消除了结果中的编译开销,并且还多次运行代码以便为您提供更具代表性的结果:
@btime dat1 = [Foo(0.0, 0.0, 0.0) for i in 1:1e6]
2.237 ms (2 allocations: 22.89 MiB)
@btime dat2 = [Bar(0.0, 0.0, 0.0) for i in 1:1e6]
6.878 ms (1000002 allocations: 38.15 MiB)
Run Code Online (Sandbox Code Playgroud)
如上所述,这对于调试分配特别有用。因为dat1我们获得了 2 个分配(一个用于Array实例本身,一个用于数组存储其数据的内存块),而dat2我们每个元素都有一个额外的分配。
| 归档时间: |
|
| 查看次数: |
523 次 |
| 最近记录: |