#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 в первом.