Список векторов медленнее в Юлии, чем R? - PullRequest
6 голосов
/ 10 января 2020

Я попытался ускорить функцию R, перенеся ее на Джулию, но, к моему удивлению, Джулия была медленнее. Функция последовательно обновляет список векторов (массив массивов в Julia). Предварительно индекс обновляемого элемента списка неизвестен, а длина нового вектора неизвестна. Я написал тестовую функцию, которая демонстрирует поведение.

Юлия

function MyTest(n)
  a = [[0.0] for i in 1:n]
    for i in 1:n
      a[i] = cumsum(ones(i))
    end  
  a
end

R

MyTest <- function(n){
  a <- as.list(rep(0, n))
  for (i in 1:n) 
    a[[i]] <- cumsum(rep(1, i))
  a
}

При установке n на 5000, 10000 и 20000, типичные вычисления время (медиана 21 теста):

  • R: 0,14, 0,45 и 1,28 секунды
  • Юлия: 0,31, 3,38 и 27,03 секунды

Я использовал windows -laptop с 64-битным Julia-1.3.1 и 64-битным R-3.6.1.

Обе эти функции используют 64-битные типы с плавающей точкой. Моя настоящая проблема связана с целыми числами, и тогда R еще более благоприятен. Но целочисленное сравнение не справедливо, так как R использует 32-битные целые и Julia 64-битные. Это то, что я могу сделать, чтобы ускорить Джулию, или Джулия действительно намного медленнее, чем R в этом случае?

Ответы [ 2 ]

3 голосов
/ 10 января 2020

Я не совсем понимаю, как вы получаете результаты теста. Предполагая, что вы хотите 32-битные целые числа, как вы сказали, тогда у нас есть

julia> function mytest(n)
           a = Vector{Vector{Int32}}(undef, n)
           for i in 1:n
               a[i] = cumsum(ones(i))
           end

           return a
       end
mytest (generic function with 1 method)

julia> @btime mytest(20000);
  1.108 s (111810 allocations: 3.73 GiB)

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

julia> function mytest(n)
           a = Vector{Vector{Int32}}(undef, n)
           @inbounds for i in 1:n
               a[i] = collect(UnitRange{Int32}(1, i))
           end

           return a
       end
mytest (generic function with 1 method)

julia> @btime mytest(20000);
  115.702 ms (35906 allocations: 765.40 MiB)

Далее Девекторизация даже не помогает:

julia> function mytest(n)
           a = Vector{Vector{Int32}}(undef, n)
           @inbounds for i in 1:n
               v = Vector{Int32}(undef, i)
               v[1] = 1
               @inbounds for j = 2:i
                   v[j] = v[j-1] + 1
               end
               a[i] = v
           end

           return a
       end
mytest (generic function with 1 method)

julia> @btime mytest(20000);
  188.856 ms (35906 allocations: 765.40 MiB)

Но с парой потоков (я предполагаю, что внутренние массивы независимы), мы снова получаем ускорение в 2 раза:

julia> Threads.nthreads()
4

julia> function mytest(n)
           a = Vector{Vector{Int32}}(undef, n)
           Threads.@threads for i in 1:n
               v = Vector{Int32}(undef, i)
               v[1] = 1
               @inbounds for j = 2:i
                   v[j] = v[j-1] + 1
               end
               a[i] = v
           end

           return a
       end
mytest (generic function with 1 method)

julia> @btime mytest(20000);
  99.718 ms (35891 allocations: 763.13 MiB)

Но это это примерно так же быстро, как второй вариант выше.

То есть для конкретного c случая cumsum. Другие внутренние функции, конечно же, работают медленнее, но могут быть одинаково многопоточными и оптимизированы одинаковыми способами, возможно, с другими результатами.

(Это на Julia 1.2, 12 ГБ ОЗУ и более старом i7.)

0 голосов
/ 10 января 2020

Возможно, R выполняет буферизацию для таких простых функций?

Вот версия Julia с буферизацией:

using Memoize
@memoize function cumsum_ones(i)
  cumsum(ones(i))
end
function MyTest2(n)
  a = Vector{Vector{Float64}}(undef, n)
    for i in 1:n
      a[i] = cumsum_ones(i)
    end
  a
end

В разогретой функции время выглядит следующим образом:

julia> @btime MyTest2(5000);
  442.500 μs (10002 allocations: 195.39 KiB)

julia> @btime MyTest2(10000);
  939.499 μs (20002 allocations: 390.70 KiB)

julia> @btime MyTest2(20000);
  3.554 ms (40002 allocations: 781.33 KiB)
...