Julia:将CSV读入向量{T}有效/类型稳定性
我有许多非常大的CSV文件,我想将其解析为自定义数据结构,以便后续处理。我目前的方法包括Julia:将CSV读入向量{T}有效/类型稳定性,julia,Julia,我有许多非常大的CSV文件,我想将其解析为自定义数据结构,以便后续处理。我目前的方法包括CSV.File,然后将每个CSV.Row转换为自定义数据结构。它适用于小测试用例,但对于大文件来说效率非常低(GC非常高)。问题在第二步,我怀疑是由于类型不稳定。下面我将提供一个模拟示例 (我是朱莉娅的新朋友,如果我误解了什么,请道歉) 定义数据结构和转换逻辑: using CSV struct Foo a::Int32 b::Float32 end Foo(csv_row::CSV.R
CSV.File
,然后将每个CSV.Row
转换为自定义数据结构。它适用于小测试用例,但对于大文件来说效率非常低(GC非常高)。问题在第二步,我怀疑是由于类型不稳定。下面我将提供一个模拟示例
(我是朱莉娅的新朋友,如果我误解了什么,请道歉)
定义数据结构和转换逻辑:
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))
上述方法可以有效地将CSV.File
转换为类型稳定的结构,从而避免在此过程中复制数据。这应该适用于大型CSV文件的情况
现在:
julia> Foo.(d.a, d.b)
2-element Array{Foo,1}:
Foo(1, 2.5f0)
Foo(3, 4.0f0)
谢谢,这确实快多了。理解为什么在第二种情况下分配了80字节,而在第一种情况下分配了0字节仍然很有趣。