Ускорение плохо написанных примеров R Юлии
Юлия примеров, чтобы сравнить производительность против Р, кажется, особенно замысловат. https://github.com/JuliaLang/julia/blob/master/test/perf/perf.R
Какова самая быстрая производительность, которую вы можете получить из двух алгоритмов ниже (предпочтительно с объяснением того, что вы изменили, чтобы сделать его более R-подобным)?
## mandel
mandel = function(z) {
c = z
maxiter = 80
for (n in 1:maxiter) {
if (Mod(z) > 2) return(n-1)
z = z^2+c
}
return(maxiter)
}
mandelperf = function() {
re = seq(-2,0.5,.1)
im = seq(-1,1,.1)
M = matrix(0.0,nrow=length(re),ncol=length(im))
count = 1
for (r in re) {
for (i in im) {
M[count] = mandel(complex(real=r,imag=i))
count = count + 1
}
}
return(M)
}
assert(sum(mandelperf()) == 14791)
## quicksort ##
qsort_kernel = function(a, lo, hi) {
i = lo
j = hi
while (i < hi) {
pivot = a[floor((lo+hi)/2)]
while (i <= j) {
while (a[i] < pivot) i = i + 1
while (a[j] > pivot) j = j - 1
if (i <= j) {
t = a[i]
a[i] = a[j]
a[j] = t
}
i = i + 1;
j = j - 1;
}
if (lo < j) qsort_kernel(a, lo, j)
lo = i
j = hi
}
return(a)
}
qsort = function(a) {
return(qsort_kernel(a, 1, length(a)))
}
sortperf = function(n) {
v = runif(n)
return(qsort(v))
}
sortperf(5000)
2 ответа:
Хм, в Примере Мандельброта матрица M имеет свои размеры транспонированы
M = matrix(0.0,nrow=length(im), ncol=length(re))
потому что он заполняется путем увеличения
count
во внутреннем цикле (последовательные значенияim
). Моя реализация создает вектор комплексных чисел вmandelperf.1
и работает на всех элементах, используя индекс и подмножество, чтобы отслеживать, какие элементы вектора еще не выполнили условиеMod(z) <= 2
mandel.1 = function(z, maxiter=80L) { c <- z result <- integer(length(z)) i <- seq_along(z) n <- 0L while (n < maxiter && length(z)) { j <- Mod(z) <= 2 if (!all(j)) { result[i[!j]] <- n i <- i[j] z <- z[j] c <- c[j] } z <- z^2 + c n <- n + 1L } result[i] <- maxiter result } mandelperf.1 = function() { re = seq(-2,0.5,.1) im = seq(-1,1,.1) mandel.1(complex(real=rep(re, each=length(im)), imaginary=im)) }
для 13-кратного ускорения (результаты равно, но не идентично, потому что оригинал возвращает числовые, а не целочисленные значения).
> library(rbenchmark) > benchmark(mandelperf(), mandelperf.1(), + columns=c("test", "elapsed", "relative"), + order="relative") test elapsed relative 2 mandelperf.1() 0.412 1.00000 1 mandelperf() 5.705 13.84709 > all.equal(sum(mandelperf()), sum(mandelperf.1())) [1] TRUE
пример quicksort на самом деле не сортирует
> set.seed(123L); qsort(sample(5)) [1] 2 4 1 3 5
но мое главное ускорение состояло в том, чтобы векторизовать раздел вокруг оси
qsort_kernel.1 = function(a) { if (length(a) < 2L) return(a) pivot <- a[floor(length(a) / 2)] c(qsort_kernel.1(a[a < pivot]), a[a == pivot], qsort_kernel.1(a[a > pivot])) } qsort.1 = function(a) { qsort_kernel.1(a) } sortperf.1 = function(n) { v = runif(n) return(qsort.1(v)) }
для 7-кратного ускорения (по сравнению с нескорректированным оригиналом)
> benchmark(sortperf(5000), sortperf.1(5000), + columns=c("test", "elapsed", "relative"), + order="relative") test elapsed relative 2 sortperf.1(5000) 6.60 1.000000 1 sortperf(5000) 47.73 7.231818
так как в исходном сравнении Джулия примерно в 30 раз быстрее, чем R для Мандела, и в 500 раз быстрее для quicksort, реализации выше все еще не очень конкурентоспособны.
ключевое слово в этом вопросе-это "алгоритм":
Какова самая быстрая производительность, которую вы можете получить из двух алгоритмы ниже (желательно с объяснением того, что вы изменили, чтобы сделать его более R-подобным)?
Как в "как быстро вы можете сделать это алгоритмы в R?"Рассматриваемые здесь алгоритмы представляют собой стандартный алгоритм итерации сложных циклов Мандельброта и стандартный рекурсивный quicksort ядро.
есть, конечно, более быстрые способы вычисления ответов на проблемы, поставленные в этих тестах, но не с использованием тех же алгоритмов. Вы можете избежать рекурсии, избежать итерации и избежать того, что еще R не очень хорошо. Но тогда вы больше не сравниваете одни и те же алгоритмы.
Если вы действительно хотели вычислить наборы Мандельброта в R или отсортировать числа, да, это не так, как вы бы написали код. Вы бы либо векторизовали его как можно больше – тем самым выталкивание всей работы в предопределенные ядра C – или просто напишите пользовательское расширение C и выполните вычисления там. В любом случае, вывод заключается в том, что R недостаточно быстр, чтобы получить действительно хорошую производительность самостоятельно – вам нужно, чтобы C выполнял большую часть работы, чтобы получить хорошую производительность.
и именно в этом суть этих тестов: в Julia вам никогда не придется полагаться на C-код, чтобы получить хорошую производительность. Вы можете просто написать то, что вы хотите сделать в чистом Юля и это будет хорошо спектакль. Если итерационный скалярный алгоритм цикла является наиболее естественным способом сделать то, что вы хотите сделать, то просто сделайте это. Если рекурсия является наиболее естественным способом решения проблемы, то это тоже нормально. Ни в коем случае вы не будете вынуждены полагаться на C для производительности – будь то через неестественную векторизацию или написание пользовательских расширений C. Конечно, ты можете написать векторизованный код, когда это естественно, как это часто бывает в линейной алгебре; и вы можете позвоните C, если у вас уже есть какая-то библиотека, которая делает то, что вы хотите. Но тебе и не нужно.
мы хотим иметь самое справедливое сравнение одних и тех же алгоритмов на разных языках:
- если у кого-то есть более быстрые версии в R, что используйте тот же алгоритм, пожалуйста, отправьте патчи!
- я считаю, что R бенчмарки на Джулия сайте уже скомпилированы байт, но если я делаю это неправильно и сравнение несправедливо к R, пожалуйста, позвольте мне знайте, и я исправлю это и обновлю тесты.