Вот мои два цента по распараллеливанию и векторизации в R. Я не буду рассматривать тензорность, поскольку у меня нет большого опыта работы с TensorFlow. Тем не менее, имея опыт работы в дифференциальной геометрии, я думаю, что для решения определенных задач лучше использовать тензор, то есть многомерные (data-) структуры.
Распараллеливание
Основа c Идея распараллеливания заключается в запуске задач одновременно . Часто, особенно когда реализовано в R, эта концепция обрабатывается с помощью многопроцессорная обработка : обычно это распределяет задачи по процессорам компьютера (или потокам см. многопоточность или проверяется это замечательно *). 1011 * ТАК ответ ). Кроме того, распараллеливание можно рассматривать как один из способов решения проблемы параллелизма : последний имеет и другие реализации, такие как асинхронное программирование .
Типичный пример распараллеливания (и также для параллелизма) следующее: Предположим, у вас есть список URL url1, url2, ...
, и вам необходимо отправить запрос GET (и дождаться ответа) каждому из них. Классическим (синхронным) способом было бы перебрать все URL, сделать запрос GET, ждать ответа, а затем (и только тогда) перейти к следующему URL.
# Dummy example list
urls <- rep('http://example.com', 7)
# Fetching the data
results <- rep(list(NA), length(urls))
for (k in seq_along(urls))
results[[k]] <- httr::GET(urls[k])
Причина, по которой это классический пример, заключается в том, что (как правило) эти запросы независимы друг от друга: теоретически нам не нужно ждать первого ответа, прежде чем делать второй запрос. Таким образом, мы могли бы отправлять эти запросы одновременно:
# Parallel
urls <- rep('http://example.com', 7)
num_cores <- parallel::detectCores() - 1
cl <- parallel::makeCluster(num_cores)
parallel::clusterEvalQ(cl, library(httr))
parallel::clusterExport(cl, varlist = c('urls'))
results <- parallel::parLapply(cl, urls, httr::GET)
parallel::stopCluster(cl)
В приведенном выше коде большинство строк о настройке, но важная строка - вторая, последняя за последней: именно здесь мы распространяем и выполняем задачи для разных ядер (ЦП).
По сути, распараллеливание тесно связано с задачами и времени.
Векторизация
Эта топи c гораздо проще. Язык R по своей природе оптимизирован для векторизованных операций: векторы, матрицы и массивы встроены в R - это не так для любого языка.
Кроме того, операции и функции также векторизованы: например, R поддерживает деление векторов 1:5 / 11:15
и в основном ведет себя так, как можно было ожидать (типичные ловушки хорошо документированы, например, 1:5 + 11:20
). Python например, имеет списки как встроенные функции, но не поддерживает (по своей природе) векторизацию: что-то вроде range(5) / range(11, 15)
выдаст ошибку (да, есть библиотеки, которые сделают это возможным).
Это не черные маги c хотя: при выполнении paste0("url_", 1:5)
al oop происходит на языке нижнего уровня C
, что делает его на несколько порядков быстрее, чем зацикливание в R. Это также, почему циклы имеют плохую репутацию в R (даже если правильное зацикливание абсолютно нормально). Вот очень наивная иллюстрация
microbenchmark::microbenchmark(
loop = {
v1 <- 1:5; v2 <- 6:10
result <- rep(NA, length(v1))
for (k in seq_along(v1))
result[k] <- v1[k] + v2[k]
},
vectorization = {
result <- 1:5 + 6:10
}
)
# Unit: nanoseconds
# expr min lq mean median uq max neval cld
# loop 1367900 1377052 1431076.98 1396951 1407551 4317901 100 b
# vectorization 400 501 1145.95 1500 1601 4001 100 a
Суть в том, что векторизация окружает (в основном) каждый угол R, и, по моему скромному мнению, это сущность элегантности R и ее самой красивой функции (обычай операторы тоже аккуратны). Например, можно создать грех Taylor Expansion с одной строкой:
f <- function(x, n = 10) sum((-1)^(0:n) * x^(2*(0:n) + 1) / factorial((2*(0:n) + 1)))
f(0)
# 0
f(pi)
# 1.034819e-11
Наконец, помимо эстетики, векторизация R - это философия, способ решения проблем: c задача, при кодировании на R вы всегда будете пытаться найти векторизованное решение проблемы, тогда как в других языках вы просто применяете циклы поверх циклов.