朱莉娅:将CSV有效地读入Vector {T} /类型稳定

我有许多非常大的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并没有什么区别。

Przemyslaw Szufel

虽然这不关注类型稳定性,但是我希望通过以下代码获得最高的性能和灵活性:

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] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章