foreach sangat lambat dengan sejumlah besar nilai

Saya mencoba menggunakan foreach untuk melakukan perhitungan paralel. Ini berfungsi dengan baik jika ada sejumlah kecil nilai yang perlu diulangi, tetapi pada titik tertentu ini menjadi sangat lambat. Berikut ini contoh sederhananya:

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 dan out2 membutuhkan waktu yang sangat lama untuk dijalankan. Tak satu pun dari mereka bahkan memunculkan banyak utas selama saya tetap menjalankannya. out3 memunculkan thread dengan segera dan berjalan sangat cepat. Apakah foreach melakukan semacam pemrosesan awal yang skalanya tidak baik? Jika ya, apakah ada perbaikan sederhana? Saya lebih suka sintaks foreach.

Saya juga harus mencatat bahwa kode sebenarnya yang saya coba paralelkan jauh lebih rumit daripada 1+1. Saya hanya menunjukkan ini sebagai contoh karena bahkan dengan kode sederhana ini, foreach tampaknya melakukan beberapa pra-pemrosesan yang sangat lambat.


person Rob Richmond    schedule 11.10.2018    source sumber
comment
Saya tidak bisa menjawab bagaimana menggunakan foreach untuk ini, tapi saya menyarankan menggunakan paket future.apply. Anda mengaturnya menggunakan library(future.apply); plan(multiprocess(workers = 3)), lalu Anda dapat menjalankan fungsi Anda dengan: future_sapply(X = 1:1e6, FUN = function(x) {1 + 1})   -  person bschneidr    schedule 12.10.2018


Jawaban (1)


sketsa forach/doParallel mengatakan (untuk kode yang jauh lebih kecil dari milik Anda):

Perhatikan baik-baik bahwa ini bukan penggunaan praktis doParallel. Ini adalah program “Halo, dunia” kami untuk komputasi paralel. Ini menguji apakah semuanya sudah terinstal dan diatur dengan benar, tapi jangan berharap itu berjalan lebih cepat daripada loop for berurutan, karena itu tidak akan terjadi! sqrt dieksekusi terlalu cepat untuk dieksekusi secara paralel, bahkan dengan jumlah iterasi yang banyak. Dengan tugas yang kecil, biaya tambahan untuk menjadwalkan tugas dan mengembalikan hasilnya bisa lebih besar daripada waktu untuk melaksanakan tugas itu sendiri, sehingga menghasilkan kinerja yang buruk. Selain itu, contoh ini tidak memanfaatkan kemampuan vektor sqrt, yang harus dimiliki untuk mendapatkan performa yang layak. Ini hanyalah ujian dan contoh pedagogi, bukan patokan.

Jadi mungkin karena sifat pengaturan Anda yang tidak lebih cepat.

Sebaliknya cobalah tanpa paralelisasi tetapi menggunakan vektorisasi:

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

Ini melakukan persis sama seperti contoh loop Anda dan dilakukan dalam hitungan detik. Dan sekarang coba ini (itu masih melakukan hal yang persis sama pada waktu yang sama:

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

Ini menambah 1e6 1s a 1 secara instan. (Kekuatan vektorisasi...)

Kombinasi foreach dengan doParallel menurut pengalaman pribadi saya jauh lebih lambat dibandingkan jika Anda menggunakan paket bioinformatika BiocParallel dari repositori Bioconda. (Saya seorang ahli bioinformatika dan dalam bioinformatika, kita sering kali menghadapi hal-hal yang memerlukan perhitungan yang berat, karena kita memiliki file data tunggal berukuran beberapa gigabyte untuk diproses - dan banyak di antaranya). Saya mencoba fungsi Anda menggunakan BiocParallel dan menggunakan semua CPU yang ditugaskan sebesar 100% (diuji dengan menjalankan htop selama eksekusi pekerjaan) semuanya membutuhkan waktu 17 detik.

Yang pasti - dengan contoh ringan Anda, ini berlaku:

biaya tambahan untuk menjadwalkan tugas dan mengembalikan hasilnya bisa lebih besar daripada waktu untuk menjalankan tugas itu sendiri

Bagaimanapun, tampaknya ia menggunakan CPU lebih menyeluruh daripada doParallel. Jadi gunakan ini, jika Anda memiliki tugas perhitungan yang berat untuk diselesaikan. Berikut kode bagaimana saya melakukannya:

# 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

Untuk informasi lebih lanjut, kunjungi sketsa paket BiocParallel. Lihatlah biokonduktor berapa banyak paket yang disediakannya dan semuanya terdokumentasi dengan baik. Saya harap ini membantu Anda untuk urusan komputasi paralel di masa depan.

person Gwang-Jin Kim    schedule 11.10.2018
comment
Terima kasih atas beberapa sarannya. Kode sebenarnya yang saya jalankan secara paralel adalah estimasi skala besar dan pastinya tidak dapat di-vektorisasi. Contoh saya hanya untuk menggambarkan bahwa foreach tampaknya melakukan beberapa pra-pemrosesan yang sangat lambat untuk indeks besar. Ia bahkan belum mulai melakukan pekerjaan paralel dalam waktu yang cukup lama. Saya bisa mencoba paket itu juga. - person Rob Richmond; 12.10.2018
comment
@Rob Richmond: Ya, saya tahu, Anda ingin menggunakannya untuk proses nyata yang perlu menggunakan lebih banyak sumber daya. Saya baru saja menjelaskan mengapa teladan khusus Anda mengecewakan Anda. Dan ya, menurut saya dengan BiocParallel Anda mungkin akan lebih puas. - person Gwang-Jin Kim; 12.10.2018
comment
Saya telah bermain dengan BiocParallel dan menganggapnya sebagai alternatif yang fantastis untuk foreach. Sepertinya ada sesuatu tentang foreach yang menyebabkannya terhenti pada sejumlah besar tugas yang belum terjawab. Karena itu, saya akan menerima jawaban Anda karena ini meningkatkan alur kerja saya secara keseluruhan. Terima kasih! - person Rob Richmond; 12.10.2018
comment
Selamat datang! Saya senang bisa membantu! Dan ya, saya pernah mencoba di awal tahun ini dengan doParallel dan foreach - dan saya juga kecewa dengan keduanya ... Terima kasih! - person Gwang-Jin Kim; 13.10.2018