Существует два уровня ответа на этот вопрос:
Уровень 1
Да, это поможет производительности кода.Смотрите, например, следующий тест:
julia> using BenchmarkTools
julia> x = Any[1 for i in 1:10^6];
julia> y = [1 for i in 1:10^6];
julia> @btime sum($x)
26.507 ms (477759 allocations: 7.29 MiB)
1000000
julia> @btime sum($y)
226.184 μs (0 allocations: 0 bytes)
1000000
Вы можете написать свою typenarrow
функцию, используя немного более простой подход, подобный следующему:
typenarrow(x) = [v for v in x]
, так как использование понимания даст векторконкретный тип (при условии, что ваш исходный вектор однороден)
Уровень 2
Это не совсем оптимально.Проблема, которая все еще остается, состоит в том, что у вас есть Dict
, который является контейнером с параметром абстрактного типа (см. https://docs.julialang.org/en/latest/manual/performance-tips/#Avoid-containers-with-abstract-type-parameters-1). Поэтому, чтобы вычисления были быстрыми, вы должны использовать барьерную функцию (см. * 1023).*https://docs.julialang.org/en/latest/manual/performance-tips/#kernel-functions-1) или используйте аннотацию типа для введенных вами переменных (см. https://docs.julialang.org/en/v1/manual/types/index.html#Type-Declarations-1).
В идеальном мире ваш Dict
будет иметь ключи и значения однородных типов, и тогда все будет максимально быстро, но если яправильно понимать ваш код значения в вашем случае не являются однородными.
РЕДАКТИРОВАТЬ
Чтобы решить проблему уровня 2, вы можете преобразовать Dict
в NamedTuple
какэто (это минимальный пример, предполагающий, что Dict
s вкладывается только в Dict
s напрямую, но его должно быть достаточно легко расширить, если вы хотите большей гибкости).
Во-первых, функция, выполняющая преобразованиевыглядит так:
function typenarrow!(d::Dict)
for k in keys(d)
if d[k] isa Array{Any,1}
d[k] = [v for v in d[k]]
elseif d[k] isa Dict
d[k] = typenarrow!(d[k])
end
end
NamedTuple{Tuple(Symbol.(keys(d)))}(values(d))
end
Теперь MWE его использования:
julia> using JSON
julia> x = """
{
"name": "John",
"age": 27,
"values": {
"v1": [1,2,3],
"v2": [1.5,2.5,3.5]
},
"v3": [1,2,3]
}
""";
julia> j1 = JSON.parse(x)
Dict{String,Any} with 4 entries:
"name" => "John"
"values" => Dict{String,Any}("v2"=>Any[1.5, 2.5, 3.5],"v1"=>Any[1, 2, 3])
"age" => 27
"v3" => Any[1, 2, 3]
julia> j2 = typenarrow!(j1)
(name = "John", values = (v2 = [1.5, 2.5, 3.5], v1 = [1, 2, 3]), age = 27, v3 = [1, 2, 3])
julia> dump(j2)
NamedTuple{(:name, :values, :age, :v3),Tuple{String,NamedTuple{(:v2, :v1),Tuple{Array{Float64,1},Array{Int64,1}}},Int64,Array{Int64,1}}}
name: String "John"
values: NamedTuple{(:v2, :v1),Tuple{Array{Float64,1},Array{Int64,1}}}
v2: Array{Float64}((3,)) [1.5, 2.5, 3.5]
v1: Array{Int64}((3,)) [1, 2, 3]
age: Int64 27
v3: Array{Int64}((3,)) [1, 2, 3]
Прелесть этого подхода в том, что Джулия будет знать все типы в j2
, поэтому, если вы передадите j2
долюбая функция в качестве параметра, все вычисления внутри этой функции будут быстрыми.
Недостатком этого подхода является то, что функция, принимающая j2
, должна быть предварительно скомпилирована, что может быть проблематично, если структура j2
огромный (так как тогда структура полученного NamedTuple
является сложной), а объем работы, выполняемой вашей функцией, относительно невелик.Но для небольших JSON-ов (небольших в смысле структуры, поскольку содержащиеся в них векторы могут быть большими - их размер не добавляет сложности), этот подход оказался эффективным в нескольких разработанных мною приложениях.