И Matlab, и S (позже R) начинали как тонкие обертки вокруг функций FORTRAN для выполнения математических задач.
В S / R циклы for "всегда" были медленными, но это было нормально, потому что обычно существуют векторизованные способы выражения проблемы.Кроме того, R имеет тысячи функций в Fortran или C, которые быстро выполняют высокоуровневые вещи.Например, функция sample
, которая делает именно то, что делает ваш цикл for, но гораздо быстрее.
Так почему же тогда MATLAB намного лучше выполняет скриптовые циклы for?Две простые причины: РЕСУРСЫ и ПРИОРИТЕТЫ.
MathWorks, которые делают MATLAB, - довольно большая компания, в которой работает около 2000 сотрудников.Несколько лет назад они решили сделать приоритетным улучшение производительности скриптов.Они наняли группу экспертов по компиляции и потратили годы на разработку компилятора Just-In-Time (JIT), который берет код скрипта и превращает его в код ассемблера.Они тоже очень хорошо поработали.Престижность им!
R с открытым исходным кодом, и основная команда R работает над улучшением R в свободное время.Люк Тирни из ядра R усердно работал и разработал пакет компилятора для R, который компилирует R-скрипты в байт-код.Однако он НЕ превращает его в ассемблерный код, но работает довольно хорошо.Слава ему!
... Но количество усилий, вложенных в компилятор R по сравнению с компилятором MATLAB, просто намного меньше, и, следовательно, результат медленнее:
system.time(GRPdur(10^6)) # 9.50 secs
# Compile the function...
f <- compiler::cmpfun(GRPdur)
system.time(f(10^6)) # 3.69 secs
КакВы можете видеть, что цикл for стал в 3 раза быстрее, скомпилировав его в байт-код.Другое отличие состоит в том, что компилятор R JIT по умолчанию не включен, как в MATLAB.
ОБНОВЛЕНИЕ Просто для записи, немного более оптимизированная версия R (основанная на алгоритме Кнута),где случайное поколение было векторизовано, как предложил @joran:
f <- function(n) {
p <- integer(n)
p[1] <- 1L
rv <- runif(n, 1, 1:n) # random integer between 1 and k
for (k in 2:n) {
r <- rv[k]
p[k] = p[r] # Swap(p(r),p(k)).
p[r] = k
}
p
}
g <- compiler::cmpfun(f)
system.time(f(1e6)) # 4.84
system.time(g(1e6)) # 0.98
# Compare to Joran's version:
system.time(GRPdur1(10^6)) # 6.43
system.time(GRPdur2(10^6)) # 1.66
... все же на несколько медленнее, чем MATLAB.Но опять же, просто используйте sample
или sample.int
, который явно превосходит MATLAB randperm
в 3 раза!
system.time(sample.int(10^6)) # 0.03