Юлия - много выделений для просмотра массива в структуре - PullRequest
0 голосов
/ 28 февраля 2019

В настоящее время я борюсь со странным поведением Джулии.Я просматриваю массив, и находится ли массив внутри структуры или нет, Джулия не ведет себя одинаково.

Есть много распределений, которые кажутся бессмысленными в случае массива внутри структуры.Если быть точным, то здесь столько же выделений, сколько и размера массива.

Вот код для репликации этой проблемы:

function test1()
    a = ones(Float32, 256)

    for i = 1:256
        a[i]
    end
end

struct X
    mat
end

function test2()
    a = X(ones(Float32, 256))

    for i = 1:256
        a.mat[i]
    end
end

function main()
    test1()
    test2()

    @time test1()
    @time test2()
end

main()

И вывод, который я получаю:

0.000002 seconds (1 allocation: 1.141 KiB)
0.000012 seconds (257 allocations: 5.141 KiB)

Сначала я подумал, что это проблема типа, но я не навязываю ее, и тип не меняется после цикла.

Спасибо за вашу помощь.

Ответы [ 2 ]

0 голосов
/ 28 февраля 2019
  1. Никогда не используйте абстрактные типы в определениях структуры.В вашем примере Юлии нужно хранить переменные указатели, а не только значения и, следовательно, снижение скорости.Вместо этого используйте параметрические типы:
julia> struct X{T}
           mat::T
           end

julia> X{Float64}.(1:3)
5-element Array{X{Float64},1}:
 X{Float64}(1.0)
 X{Float64}(2.0)
 X{Float64}(3.0)
Если вы не уверены, рассмотрите возможность использования макроса @code_warntype, чтобы увидеть, где компилятор Julia не может правильно идентифицировать типы.
0 голосов
/ 28 февраля 2019

Вам необходимо указать тип mat в вашем struct.В противном случае ваши функции, использующие X, не будут специализироваться и будут достаточно оптимизированы.

Поля без аннотации типа по умолчанию имеют значение Любой и могут соответственно содержать любой тип значения.https://docs.julialang.org/en/v1/manual/types/index.html#Composite-Types-1

Изменение определения структуры на

struct X
    mat::Vector{Float32}
end

решит проблему.Результаты теперь таковы:

  0.000000 seconds (1 allocation: 1.141 KiB)
  0.000000 seconds (1 allocation: 1.141 KiB)

Вы можете увидеть эффект через макрос @code_warntype, если измените одну вещь в своем коде.

for i = 1:256
    a.mat[i]
end

Эта часть на самом деле мало что делает,Чтобы увидеть эффект с @code_warntype, измените эту строку в вашем старом коде на

for i = 1:256
    a.mat[i] += 1.
end

Результат @code_warntype даст Any красного цвета, которого вам обычно следует избегать.Причина в том, что тип mat не известен во время компиляции.

> @code_warntype test2() # your test2() with old X def
Body::Nothing
1 ─ %1  = $(Expr(:foreigncall, :(:jl_alloc_array_1d), Array{Float32,1}, svec(Any, Int64), :(:ccall), 2, Array{Float32,1}, 256, 256))::Array{Float32,1}
│   %2  = invoke Base.fill!(%1::Array{Float32,1}, 1.0f0::Float32)::Array{Float32,1}
└──       goto #7 if not true
2 ┄ %4  = φ (#1 => 1, #6 => %14)::Int64
│   %5  = φ (#1 => 1, #6 => %15)::Int64
│   %6  = (Base.getindex)(%2, %4)::Any <------ See here
│   %7  = (%6 + 1.0)::Any
│         (Base.setindex!)(%2, %7, %4)
│   %9  = (%5 === 256)::Bool
└──       goto #4 if not %9
3 ─       goto #5
4 ─ %12 = (Base.add_int)(%5, 1)::Int64
└──       goto #5
5 ┄ %14 = φ (#4 => %12)::Int64
│   %15 = φ (#4 => %12)::Int64
│   %16 = φ (#3 => true, #4 => false)::Bool
│   %17 = (Base.not_int)(%16)::Bool
└──       goto #7 if not %17
6 ─       goto #2
7 ┄       return

Теперь с новым определением X вы увидите в результате @code_warntype вывод каждого типа.

Возможно, вы захотите использовать Параметрические типы, если вы хотите, чтобы X.mat содержал другие типы Vector s или значений.С параметрическими типами компилятор все еще сможет оптимизировать ваши функции, так как тип будет известен во время компиляции.Я действительно рекомендую вам прочитать соответствующую инструкцию для типов и советов по производительности .

...