foreach очень медленный с большим количеством значений - PullRequest
0 голосов
/ 11 октября 2018

Я пытаюсь использовать foreach для параллельных вычислений.Он отлично работает, если есть небольшое количество значений для перебора, но в какой-то момент он становится невероятно медленным.Вот простой пример:

library(foreach)
library(doParallel)

registerDoParallel(8)

out1 <- foreach(idx=1:1e6) %do%
    {
        1+1
    }

out2 <- foreach(idx=1:1e6) %dopar%
    {
        1+1
    }

out3 <- mclapply(1:1e6,
                 function(x) 1+1,
                 mc.cores=20)

out1 и out2 занимают невероятно много времени для запуска.Ни один из них даже не создает несколько потоков, пока я их поддерживаю.out3 порождает темы почти сразу и работает очень быстро.foreach выполняет какую-то начальную обработку, которая плохо масштабируется?Если да, то есть ли простое исправление?Я действительно предпочитаю синтаксис foreach.

Я также должен отметить, что фактический код, который я пытаюсь распараллелить, существенно сложнее, чем 1 + 1.Я показываю это только в качестве примера, потому что даже с этим простым кодом foreach, кажется, выполняет некоторую предобработку, которая невероятно медленная.

1 Ответ

0 голосов
/ 11 октября 2018

виньетка forach / doParallel говорит (коду, намного меньшему вашего):

Обратите внимание, что это не практическое использование doParallel.Это наша программа «Hello, world» для параллельных вычислений.Он проверяет, что все установлено и настроено правильно, но не ожидайте, что он будет работать быстрее, чем последовательный цикл for, потому что это не так!sqrt выполняется слишком быстро, чтобы его можно было выполнять параллельно, даже с большим количеством итераций.Для небольших задач накладные расходы по планированию задачи и возвращению результата могут быть больше, чем время выполнения самой задачи, что приводит к снижению производительности.Кроме того, в этом примере не используются векторные возможности sqrt, что должно обеспечить приличную производительность.Это всего лишь тестовый и педагогический пример, а не эталонный тест.

Таким образом, в вашей среде может быть не быстрее.

Вместо этого попробуйте без распараллеливания, ноиспользуя векторизацию:

q <- sapply(1:1e6, function(x) 1 + 1 )

Он делает то же самое, что и ваш пример цикла, и выполняется за секунду.А теперь попробуйте это (он делает то же самое точно в то же самое время:

x <- rep(1, n=1e6)
r <- x + 1

Это мгновенно добавляет к 1e6 1 sa 1. (Мощность векторизации ...)

Сочетание foreach с doParallel по моему личному опыту намного медленнее, чем если бы вы использовали пакет биоинформатики BiocParallel из хранилища Bioconda. (Я биоинформатик, и в биоинформатике у нас очень часто вычисление-тяжелые вещи, так как у нас есть отдельные файлы данных по несколько гигабайт для обработки - и многие из них.) Я опробовал вашу функцию, используя BiocParallel, и она использует все назначенные процессоры на 100% (проверено при запуске htop во время выполнения задания)все это заняло 17 секунд.

Точно - с вашим легким примером это применимо:

накладные расходы на планирование задачи и возврат результата могут быть больше, чем время наВыполните само задание

В любом случае, кажется, что процессоры используются более тщательно, чем doParallel. Так что используйте это, если у вас есть расчетытяжелые задачи должны быть выполнены.Вот код, как я это сделал:

# For bioconductor packages, the best is to install this:
install.packages("BiocManager")

# Then activate the installer
require(BiocManager)

# Now, with the `install()` function in this package, you can install
# conveniently Bioconductor packages like `BiocParallel`
install("BiocParallel")

# then, activate it
require(BiocParallel)

# initiate cores:
bpparam <- bpparam <- SnowParam(workers=4, type="SOCK") # 4 or take more CPUs

# prepare the function you want to parallelize
FUN <- function(x) { 1 + 1 }

# and now you can call the function using `bplapply()`
# the loop parallelizing function in BiocParallel.
s <- bplapply(1:1e6, FUN, BPPARAM=bpparam) # each value of 1:1e6 is given to 
# FUN, note you have to pass the SOCK cluster (bpparam) for the 
# parallelization

Для получения дополнительной информации перейдите к виньетка пакета BiocParallel .Посмотрите на bioconductor, сколько пакетов он предоставляет и все хорошо документировано.Я надеюсь, что это поможет вам в ваших будущих параллельных вычислениях.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...