Джулия: эффективно считывать CSV в вектор {T} / стабильность типа

#julia

#джулия

Вопрос:

У меня есть несколько очень больших файлов CSV, которые я хотел бы разобрать на пользовательские структуры данных для последующей обработки. Мой текущий подход включает 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,bn1,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 не имеет значения.

Ответ №1:

Хотя это не касается стабильности типа, я бы ожидал от следующего кода высочайшей производительности в сочетании с гибкостью:

 d = DataFrame!(CSV.File(Vector{UInt8}("a,bn1,2.5n3,4.0"); threaded = false))
  

Вышеизложенное эффективно преобразует a CSV.File в стабильную структуру типа, дополнительно избегая копирования данных в этом процессе. Это должно сработать для вашего случая с огромными файлами CSV.

И теперь:

 julia> Foo.(d.a, d.b)
2-element Array{Foo,1}:
 Foo(1, 2.5f0)
 Foo(3, 4.0f0)
  

Комментарии:

1. Спасибо, это действительно намного быстрее. Все равно было бы интересно понять, почему во втором случае выделено 80 байт по сравнению с 0 в первом.