При использовании mclapply каждое отдельное ядро ​​работает медленнее, чем его непараллельная версия.

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

Вкратце, почему в следующем примере большинство значений user в t меньше, чем в mc_t ? Моя машина имеет 32 ГБ памяти, 2 процессора с 4 ядрами и всего 8 гиперпотоков.

system.time({t = lapply(1:4,function(i) {
    m = matrix(1:10^6,ncol=100)
    t = system.time({
        m%*%t(m)
    })
    return(t)
})})


library(multicore)
system.time({
    mc_t = mclapply(1:4,function(m){
        m = matrix(1:10^6,ncol=100)
        t = system.time({
            m%*%t(m)
        })
        return(t)
    },mc.cores=4)
})

> t
[[1]]
user  system elapsed
11.136   0.548  11.703

[[2]]
user  system elapsed
11.533   0.548  12.098

[[3]]
user  system elapsed
11.665   0.432  12.115

[[4]]
user  system elapsed
11.580   0.512  12.115

> mc_t
[[1]]
user  system elapsed
16.677   0.496  17.199

[[2]]
user  system elapsed
16.741   0.428  17.198

[[3]]
user  system elapsed
16.653   0.520  17.198

[[4]]
user  system elapsed
11.056   0.444  11.520

И sessionInfo():

> sessionInfo()
R version 3.0.2 (2013-09-25)
Platform: x86_64-pc-linux-gnu (64-bit)

locale:
[1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C
[3] LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8
[5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8
[7] LC_PAPER=en_US.UTF-8       LC_NAME=C
[9] LC_ADDRESS=C               LC_TELEPHONE=C
[11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C

attached base packages:
    [1] stats     graphics  grDevices utils     datasets  methods   base

other attached packages:
    [1] multicore_0.1-7

Чтобы уточнить: извините, что мое описание может быть двусмысленным. Я понимаю, что параллель все же быстрее для всей миссии. Однако счетчик времени есть только в функции для расчета, время установки оверхеда для каждого дочернего процесса в mclapply не учитывается. Поэтому я все еще не понимаю, почему этот чистый расчет (т.е. m%*%t(m)) выполняется медленнее.


person TomHall    schedule 12.02.2014    source источник
comment
Мое безумное предположение состоит в том, что разница заключается в накладных расходах на установку для каждого дочернего процесса. На самом деле это не то, как можно использовать многоядерность: попробуйте сравнить одно ядро, выполняющее matrix(4*10^6,4000,1000), с mcapply, которое создает четыре матрицы 1000x1000 и объединяет возвращаемые объекты.   -  person Carl Witthoft    schedule 12.02.2014
comment
@CarlWitthoft прав. Вы просто измеряете накладные расходы на связь между ядрами. Моя интерпретация результатов заключается в том, что с одним ядром ваш код выполняется примерно 12 секунд. Таким образом, запуск 4 раза займет ~ 48 секунд. С многоядерным весь процесс занимает 16 секунд для 4 результатов. Эти дополнительные 4 секунды — это штраф, который вы получаете при обмене данными между ядрами.   -  person Andrie    schedule 12.02.2014
comment
Спасибо @CarlWitthoft и @Andrie, и мне очень жаль, что я не описал это ясно. Я понимаю, что параллель все же быстрее для всей миссии. Однако счетчик времени находится только в функции для расчета, время установки оверхеда для каждого дочернего процесса в mclapply не учитывается. Так что я все еще не понимаю, почему этот чистый шаг вычисления медленнее.   -  person TomHall    schedule 12.02.2014


Ответы (2)


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

Обратите внимание, что оператор %*% будет использовать несколько ядер, если в вашей установке R используется многопоточная математическая библиотека, такая как MKL или ATLAS. Используя несколько процессов вдобавок к этому, вы можете иметь намного больше потоков, чем ядер, что снижает вашу производительность.

person Steve Weston    schedule 12.02.2014
comment
Я вручную запустил несколько команд Rscript и сравнил время их выполнения. Поскольку они автоматически запускались на разных ядрах, я думаю, вы правы. Но не могли бы вы объяснить свой комментарий к «библиотеке BLAS»? Я не вижу связи между BLAS и разницей в производительности. Спасибо! - person TomHall; 13.02.2014

Теоретически наилучшее возможное ускорение параллельного алгоритма рассчитывается как

S(n) = T(1) / T(n) = 1 / (P + (1 - P) / n)

где T(n) — время, необходимое для выполнения задачи с использованием n параллельных процессов, а P — доля строго последовательного выполнения всей задачи. С помощью этой формулы давайте вычислим теоретическое максимально возможное ускорение параллельного алгоритма с 4 процессорами, учитывая, что задача выполняется за 10 секунд на одном процессоре.

1 / (0.5 + (1-0.5)/4) = 1.6x

Используя эту информацию, мы можем сказать, что каждый процессор медленнее, чем однопроцессорная версия алгоритма, потому что ускорение менее 4x. Но это просто то, как это работает. Эта ситуация называется законом Амдала. Закон Амдала дает нам оценку максимально возможного ускорения и не учитывает накладные расходы. Вы можете прочитать его далее здесь.

Для вычисления времени вместо скорости разгона используем формулу

T(n) = T(1)*(P + (1-P)/n)

Давайте вычислим наилучшее возможное время выполнения задачи, которая занимает 10 секунд на одном процессоре.

T(4) = 10 * (0.5 + (1 - 0.5)/4) = 6.25
person boyaronur    schedule 18.01.2019