2013-06-03 3 views
3

Я уже некоторое время изучаю R, и натолкнулся на множество советов по программированию, типа себя, для векторизации операций. Будучи программистом, мне интересно, почему и как это происходит быстрее. Пример:Почему ускорение векторизации

n = 10^7 
# populate with random nos 
v=runif(n) 
system.time({vv<-v*v; m<-mean(vv)}); m 
system.time({for(i in 1:length(v)) { vv[i]<-v[i]*v[i] }; m<-mean(vv)}); m 

Это дало

user system elapsed 
    0.04 0.01 0.07 
[1] 0.3332091 

    user system elapsed 
    36.68 0.02 36.69 
[1] 0.3332091 

Наиболее очевидная вещь, чтобы рассмотреть, что мы бежим машинный код, т.е. машинный код скомпилирован с C или C++, а не интерпретировать код, как показано на рисунке благодаря большой разнице в времени пользователя между двумя примерами (примерно на 3 порядка). Но есть ли что-нибудь еще? Например, делает ли R:

  • Хитроумные структуры данных, например. умные способы хранения разреженных векторов или матриц, чтобы мы только умножали, когда нам нужно?

  • ленивая оценка, например. на матрице умножить, не оценивайте ячейки до тех пор, пока вам и когда вам нужно.

  • Параллельная обработка.

  • Что-то еще.

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

# populate with random nos 
v<-runif(n) 
system.time({m<-v%*%v/n}); m 
# populate with runs of 1 followed by 99 0s 
v <-rep(rep(c(1,rep(0,99)),n/100)) 
system.time({m<-v%*%v/n}); m 
# populate with 0s 
v <-rep(0,n) 
system.time({m<-v%*%v/n}); m 

Однако не было никакой существенной разницы во времени (около 0,09 истекшего)

(Аналогичный вопрос для Matlab: Why does vectorized code run faster than for loops in MATLAB?)

ответ

9

Наиболее очевидная вещь, чтобы рассмотреть, что мы бежим машинный код, т.е. машинный код скомпилирован с C или C++, а не интерпретировать код

Это большинство из них. Другой большой компонент состоит в том, что, поскольку R-код является функциональным в своей конструктивной парадигме, функции (попытка) не имеют побочных эффектов, а это значит, что в некоторых (но, возможно, не все) R делает попыткой быть эффективными по этому поводу) экземпляры, вызывающие [<- в поле a for, приводят к необходимости копировать весь объект. То, что может замедляться.

Небольшая заметка: R имеет довольно широкую функциональность для эффективного управления матричными структурами sparse, но они не являются «стандартными».

+0

Спасибо, я не совсем понял, что вы подразумеваете под функциями в цикле, вы могли бы предоставить простой пример или ссылку (мне в целом интересно, как функциональная сторона R влияет на вещи с точки зрения пользователя view - пока все, что я сделал, было довольно процедурным по стилю). – TooTone

+0

@TooTone 'x <- 1:10; tracemem (х); x [5] <- 1' был бы, наверное, самым простым примером. – joran

+0

Это тонкое. Я понятия не имел, что происходит (есть дискуссионная тема) (http://r.789695.n4.nabble.com/Why-is-vector-assignment-in-R-recreates-the-entire-vector- td2403402.html), в котором отмечается, что R в основном оптимизирует эти копии, если кто-то еще захочет изучить это дальше). Я провел некоторое тестирование, и в моем коде с циклом 'for',' vv' копируется один раз, когда 'i == 1', но это не влияет на производительность. – TooTone

4

Что касается параллельной обработки, то внебиржевой R не делает никакого параллаха el обработка. Конечно, есть встроенный пакет parallel, но вам нужно адаптировать свой код к использованию, например. mclapply использовать параллельную обработку. Существуют варианты, позволяющие вычислять вашу линейную алгебру параллельно с помощью специальной версии blas, но это стандартно не используется в R, хотя заставить ее работать не так сложно.

+0

Спасибо, я предполагаю, что параллельная обработка - это один из способов, с помощью которых Revolution Analytics R (любить их или ненавидеть) пытается сделать бизнес. – TooTone

7

В обоих примерах используется как интерпретируемый код, так и собственный код.Разница в том, что во втором вы выполняете цикл на уровне R, в результате чего появляется еще много вызовов функций, которые все нужно интерпретировать, а затем вызывается код C. В вашем первом примере цикл происходит внутри скомпилированного кода, и, следовательно, R гораздо меньше интерпретирует, гораздо меньше вызовов R-кода и гораздо меньше вызовов скомпилированного кода.

Смежные вопросы