Удвойте распределение, сообщаемое @time для больших векторов в Юлии - PullRequest
3 голосов
/ 16 октября 2019

Рассмотрим следующую простую программу в Julia:

function foo_time(x)
    @time x.^2
    return nothing
end
n = 1000;
foo_time(collect(1:n));

Если я запусту это в своей консоли, то @time сообщит о 1 выделении, чего я и ожидаю. Однако, если я изменю n на 10000, то @time сообщит о 2 выделениях.

Более того, если я объединю функции в цепочку без объединения синтаксических циклов (другими словами, без точек), тогда япохоже, удвоить ожидаемые отчисления. Например, запись (x + x).^2 + x вместо x.^2 дает 3 выделения с n = 1000, но дает 6 распределений с n = 10000. (Шаблон не является строго продолжающимся, хотя: например, (x + x + x).^2 дает только 5 выделений для n = 10000.)

Почему размер вектора должен влиять на количество выделений? Что здесь происходит под капотом?

Это происходит как в консоли JupyterLab, так и в обычном Julia REPL.

Ответы [ 2 ]

4 голосов
/ 16 октября 2019

Почему существует одно распределение с маленькими векторами и два распределения с большими векторами?

Действительно, это не имеет значения, и это внутренняя деталь того, как работают массивы. По сути, в Julia Array есть две части: внутренний заголовок (который отслеживает размерность массива, тип элемента и т. Д.) И сами данные. Когда массивы маленькие, есть преимущество в объединении этих двух сегментов данных, но когда массивы большие, есть преимущество в том, что они разделены. Это не вещательная вещь, это просто вещь распределения массива:

julia> f(n) = (@time Vector{Int}(undef, n); nothing)
f (generic function with 1 method)

julia> f(2048)
  0.000003 seconds (1 allocation: 16.125 KiB)

julia> f(2049)
  0.000003 seconds (2 allocations: 16.141 KiB)

Тогда, надеюсь, вы поймете, почему это приводит к удвоению количества выделений для больших массивов, когда задействованы временные ресурсы - есть один длязаголовок каждого массива и один для данных каждого массива.

Короче - не беспокойтесь о количестве выделений. Есть моменты, когда распределение может реально улучшить производительность. Однако, когда это необходимо, вы видите огромное количество выделений, особенно если вы видите, что они пропорциональны количеству элементов в массиве.

1 голос
/ 17 октября 2019

Я согласен с Мэттом, для этой простой задачи количество распределений не является хорошим показателем.

Если вы хотите погрузиться в детали и точно понять, как ваш код компилируется и выполняется, я предлагаю вам макросы тезисов @code_llvm, @code_lowered, @code_native, @code_typed и @code_warntype. Все тонкости между этими макросами подробно объясняются в документе Julia здесь и там .

julia> f(x) = x.^2
f (generic function with 1 method)

julia> @code_lowered f(randn(10000))
CodeInfo(
1 ─ %1 = (Core.apply_type)(Base.Val, 2)
│   %2 = (%1)()
│   %3 = (Base.broadcasted)(Base.literal_pow, Main.:^, x, %2)
│   %4 = (Base.materialize)(%3)
└──      return %4
)

julia> f2(x) = (x + x).^2 + x
f2 (generic function with 1 method)

julia> @code_lowered f2(randn(10000))
CodeInfo(
1 ─ %1 = x + x
│   %2 = (Core.apply_type)(Base.Val, 2)
│   %3 = (%2)()
│   %4 = (Base.broadcasted)(Base.literal_pow, Main.:^, %1, %3)
│   %5 = (Base.materialize)(%4)
│   %6 = %5 + x
└──      return %6
)
...