我有许多非常大的CSV文件,我想将其解析为自定义数据结构以进行后续处理。我当前的方法涉及CSV.File
然后将它们转换CSV.Row
成自定义数据结构。它适用于小型测试用例,但不适用于大型文件(GC非常高)。问题出在第二步,我怀疑是由于类型不稳定。我在下面提供一个模拟示例。
(我是Julia的新手,因此如果我误解了一些内容,我们深表歉意)
定义数据结构和转换逻辑:
using CSV
struct Foo
a::Int32
b::Float32
end
Foo(csv_row::CSV.Row) = Foo(csv_row.a, csv_row.b)
使用默认构造函数将导致0分配:
julia> @allocated foo1 = Foo(1, 2.5)
0
但是,CSV.Row
突然创建对象时,会分配80个字节:
julia> data = CSV.File(Vector{UInt8}("a,b\n1,2.5"); threaded = false)
1-element CSV.File{false}:
CSV.Row: (a = 1, b = 2.5f0)
julia> @allocated foo2 = Foo(data[1])
80
在第一种情况下,所有类型都是稳定的:
julia> @code_warntype Foo(1, 2)
Variables
#self#::Core.Compiler.Const(Foo, false)
a::Int64
b::Int64
Body::Foo
1 ─ %1 = Main.Foo::Core.Compiler.Const(Foo, false)
│ %2 = Core.fieldtype(%1, 1)::Core.Compiler.Const(Int32, false)
│ %3 = Base.convert(%2, a)::Int32
│ %4 = Core.fieldtype(%1, 2)::Core.Compiler.Const(Float32, false)
│ %5 = Base.convert(%4, b)::Float32
│ %6 = %new(%1, %3, %5)::Foo
└── return %6
在第二种情况下则不是:
julia> @code_warntype Foo(data[1])
Variables
#self#::Core.Compiler.Const(Foo, false)
csv_row::CSV.Row
Body::Foo
1 ─ %1 = Base.getproperty(csv_row, :a)::Any
│ %2 = Base.getproperty(csv_row, :b)::Any
│ %3 = Main.Foo(%1, %2)::Foo
└── return %3
所以我想我的问题是:如何使第二种情况保持类型稳定并避免分配?
明确地提供类型CSV.File
并没有什么区别。
虽然这不关注类型稳定性,但是我希望通过以下代码获得最高的性能和灵活性:
d = DataFrame!(CSV.File(Vector{UInt8}("a,b\n1,2.5\n3,4.0"); threaded = false))
以上有效地将aCSV.File
转换为类型稳定的结构,另外避免了此过程中的数据复制。这适用于大型CSV文件的情况。
现在:
julia> Foo.(d.a, d.b)
2-element Array{Foo,1}:
Foo(1, 2.5f0)
Foo(3, 4.0f0)
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句