Die Julia-Beispiele zum Vergleich der Leistung mit R scheinen besonders kompliziert zu sein . https://github.com/JuliaLang/julia/blob/master/test/perf/perf.R
Was ist die schnellste Leistung, die Sie mit den beiden folgenden Algorithmen erzielen können (vorzugsweise mit einer Erklärung dessen, was Sie geändert haben, um es R-ähnlicher zu machen)?
## 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)
r
optimization
julia
Ari B. Friedman
quelle
quelle
Antworten:
Hmm, im Mandelbrot-Beispiel sind die Abmessungen der Matrix M transponiert
M = matrix(0.0,nrow=length(im), ncol=length(re))
weil es durch Inkrementieren
count
in der inneren Schleife gefüllt wird (aufeinanderfolgende Werte vonim
). Meine Implementierung erstellt einen Vektor komplexer Zahlen inmandelperf.1
und bearbeitet alle Elemente, wobei ein Index und eine Teilmenge verwendet werden, um zu verfolgen, welche Elemente des Vektors die Bedingung noch nicht erfüllt habenMod(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)) }
für eine 13-fache Beschleunigung (die Ergebnisse sind gleich, aber nicht identisch, da das Original eher numerische als ganzzahlige Werte zurückgibt).
> 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
Das Quicksort-Beispiel sortiert nicht wirklich
> set.seed(123L); qsort(sample(5)) [1] 2 4 1 3 5
Meine Hauptbeschleunigung bestand jedoch darin, die Partition um den Drehpunkt herum zu vektorisieren
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)) }
für eine 7-fache Beschleunigung (im Vergleich zum unkorrigierten Original)
> 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
Da Julia im ursprünglichen Vergleich etwa 30-mal schneller als R für Mandel und 500-mal schneller für Quicksort ist, sind die obigen Implementierungen immer noch nicht wirklich wettbewerbsfähig.
quelle
qsort_kernel.1
immer noch eine Rekursion durchgeführt wird und R darin nicht gut ist. Um es schneller laufen zu lassen, konvertieren Sie es mithilfe eines Stapels in Schleifen.Das Schlüsselwort in dieser Frage ist "Algorithmus":
Wie in "Wie schnell können Sie diese machen Algorithmen in R erstellen?" Die hier fraglichen Algorithmen sind der Standard-Mandelbrot-Algorithmus für komplexe Schleifeniterationen und der Standard-rekursive Quicksort-Kernel.
Es gibt sicherlich schnellere Möglichkeiten, die Antworten auf die in diesen Benchmarks aufgeworfenen Probleme zu berechnen - jedoch nicht mit denselben Algorithmen. Sie können Rekursion vermeiden, Iteration vermeiden und alles vermeiden, was R sonst nicht kann. Aber dann vergleichen Sie nicht mehr dieselben Algorithmen.
Wenn Sie wirklich Mandelbrot-Mengen in R berechnen oder Zahlen sortieren wollten, ja, so würden Sie den Code nicht schreiben. Sie würden es entweder so weit wie möglich vektorisieren und so die gesamte Arbeit in vordefinierte C-Kernel verschieben - oder einfach eine benutzerdefinierte C-Erweiterung schreiben und dort die Berechnung durchführen. In beiden Fällen ist die Schlussfolgerung, dass R nicht schnell genug ist, um selbst eine wirklich gute Leistung zu erzielen. Sie müssen C den größten Teil der Arbeit erledigen lassen, um eine gute Leistung zu erzielen.
Und genau darum geht es bei diesen Benchmarks: In Julia muss man sich nie auf C-Code verlassen, um eine gute Leistung zu erzielen. Sie können einfach schreiben, was Sie in reiner Julia tun möchten, und es wird eine gute Leistung haben. Wenn ein iterativer Skalarschleifenalgorithmus der natürlichste Weg ist, das zu tun, was Sie tun möchten, dann tun Sie das einfach. Wenn Rekursion der natürlichste Weg ist, um das Problem zu lösen, ist das auch in Ordnung. Sie werden zu keinem Zeitpunkt gezwungen sein, sich für die Leistung auf C zu verlassen - sei es durch unnatürliche Vektorisierung oder durch das Schreiben benutzerdefinierter C-Erweiterungen. Natürlich Sie können Code vektorisiert schreiben , wenn es natürlich ist, wie es oft in der linearen Algebra ist; und Sie können C aufrufen, wenn Sie bereits eine Bibliothek haben, die das tut, was Sie wollen. Aber das musst du nicht.
Wir wollen einen möglichst fairen Vergleich derselben Algorithmen zwischen verschiedenen Sprachen:
quelle