Я знаю, что циклы выполняются медленно, R
и вместо этого я должен попытаться сделать что-то векторизованным.
Но почему? Почему петли медленные и apply
быстрые? apply
вызывает несколько подфункций - это не кажется быстрым.
Обновление: извините, вопрос был некорректным. Я путал векторизацию с apply
. Мой вопрос должен был быть таким:
"Почему векторизация быстрее?"
performance
r
apply
изоморфизмы
источник
источник
system.time
начнутся войны в ответах ...Ответы:
Циклы в R медленные по той же причине, по которой любой интерпретируемый язык работает медленно: каждая операция несет с собой много лишнего багажа.
Посмотрите
R_execClosure
вeval.c
(это функция , которая вызывается , чтобы вызвать определенную пользователем функцию). Он состоит из почти 100 строк и выполняет всевозможные операции - создание среды для выполнения, присвоение аргументов среде и т. Д.Подумайте, насколько меньше происходит, когда вы вызываете функцию в C (вставлять аргументы в стек, переходить, выдавать аргументы).
Вот почему вы получаете такие моменты времени (как указал Джоран в комментарии, на самом деле
apply
это не так быстро; это внутренний цикл C,mean
который является быстрым.apply
Это просто обычный старый код R):A = matrix(as.numeric(1:100000))
Используя цикл: 0,342 секунды:
system.time({ Sum = 0 for (i in seq_along(A)) { Sum = Sum + A[[i]] } Sum })
Используя сумму: неизмеримо малая:
Это немного сбивает с толку, потому что асимптотически цикл ничем не хуже
sum
; нет никакой практической причины, по которой он должен быть медленным; он просто выполняет больше дополнительной работы с каждой итерацией.Итак, рассмотрим:
# 0.370 seconds system.time({ I = 0 while (I < 100000) { 10 I = I + 1 } }) # 0.743 seconds -- double the time just adding parentheses system.time({ I = 0 while (I < 100000) { ((((((((((10)))))))))) I = I + 1 } })
(Этот пример был обнаружен Рэдфордом Нилом )
Потому что
(
в R - это оператор, который фактически требует поиска имени каждый раз, когда вы его используете:> `(` = function(x) 2 > (3) [1] 2
Или, как правило, интерпретируемые операции (на любом языке) содержат больше шагов. Конечно, эти шаги также приносят пользу: вы не смогли бы проделать такой
(
трюк в C.источник
for()
петель? Они вообще не делают то же самое.for()
Цикл итерации по каждому элементуA
и их суммирования.apply()
Вызов проходит весь векторA[,1]
(вашA
имеет один столбец) в vectorised функцииmean()
. Я не вижу, как это помогает дискуссии и просто запутывает ситуацию.for()
vs.apply()
Я думаю, вам следует удалить этот пример, поскольку, хотя суммирование является большой частью вычисления среднего, все, что действительно показывает ваш пример, - это скорость векторизованной функцииmean()
по C-подобной итерации по элементам.Не всегда петли бывают медленными и
apply
быстрыми. Об этом есть хорошее обсуждение в выпуске R News за май 2008 года :В разделе "Петли!" (начиная со стр. 48) они говорят:
Далее они предлагают:
У них есть простой пример, когда
for
цикл занимает 1,3 секунды, ноapply
заканчивается нехватка памяти.источник
Единственный ответ на поставленный вопрос: Циклы не медленные, если вам нужно перебрать набор данных, выполняющих некоторую функцию, и эта функция или операция не векторизованы. В
for()
общем случае цикл будет таким же быстрым, какapply()
, но, возможно, немного медленнее, чемlapply()
вызов. Последний пункт хорошо освещен в SO, например, в этом ответе , и применяется, если код, участвующий в настройке и работе цикла, составляет значительную часть общей вычислительной нагрузки цикла .Многие люди думают, что
for()
циклы медленные, потому что они, как пользователь, пишут плохой код. В общем (хотя есть несколько исключений), если вам нужно расширить / увеличить объект, это тоже потребует копирования, так что у вас будут накладные расходы на копирование и увеличение объекта. Это не ограничивается только циклами, но если вы копируете / увеличиваете на каждой итерации цикла, конечно, цикл будет медленным, потому что вы выполняете много операций копирования / увеличения.Общая идиома использования
for()
циклов в R заключается в том, что вы выделяете необходимое хранилище до начала цикла, а затем заполняете выделенный таким образом объект. Если вы будете следовать этой идиоме, циклы не будут медленными. Это то, чтоapply()
вам удается, но это просто скрыто от глаз.Конечно, если для операции, которую вы выполняете с
for()
циклом, существует векторизованная функция , не делайте этого . Точно так же не используйте иapply()
т. Д., Если существует векторизованная функция (напримерapply(foo, 2, mean)
, лучше выполнять черезcolMeans(foo)
).источник
Просто для сравнения (не вдавайтесь в подробности!): Я запустил (очень) простой цикл for в R и в JavaScript в Chrome и IE 8. Обратите внимание, что Chrome выполняет компиляцию в собственный код, а R с компилятором пакет компилируется в байт-код.
# In R 2.13.1, this took 500 ms f <- function() { sum<-0.5; for(i in 1:1000000) sum<-sum+i; sum } system.time( f() ) # And the compiled version took 130 ms library(compiler) g <- cmpfun(f) system.time( g() )
@ Гэвин Симпсон: Кстати, в S-Plus потребовалось 1162 мс ...
И «тот же» код, что и в JavaScript:
// In IE8, this took 282 ms // In Chrome 14.0, this took 4 ms function f() { var sum = 0.5; for(i=1; i<=1000000; ++i) sum = sum + i; return sum; } var start = new Date().getTime(); f(); time = new Date().getTime() - start;
источник