foreach ช้ามากโดยมีค่าจำนวนมาก

ฉันกำลังพยายามใช้ 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 นี้ดูเหมือนว่าจะทำการประมวลผลล่วงหน้าบางอย่างที่ช้าอย่างไม่น่าเชื่อ


person Rob Richmond    schedule 11.10.2018    source แหล่งที่มา
comment
ฉันไม่สามารถตอบวิธีใช้ foreach ได้ แต่ฉันแนะนำให้ใช้แพ็คเกจ future.apply ได้ คุณตั้งค่าโดยใช้ library(future.apply); plan(multiprocess(workers = 3)) จากนั้นคุณสามารถดำเนินการฟังก์ชันของคุณด้วย: future_sapply(X = 1:1e6, FUN = function(x) {1 + 1})   -  person bschneidr    schedule 12.10.2018


คำตอบ (1)


forach/doParallel บทความสั้นบอกว่า (เป็นโค้ดที่เล็กกว่าของคุณมาก):

โปรดทราบว่านี่ไม่ใช่การใช้งาน doParallel ในทางปฏิบัติ นี่คือโปรแกรม “Hello, world” ของเราสำหรับการประมวลผลแบบขนาน จะทดสอบว่าทุกอย่างได้รับการติดตั้งและตั้งค่าอย่างถูกต้อง แต่อย่าคาดหวังว่ามันจะทำงานเร็วกว่าลำดับ for loop เพราะมันจะไม่เป็นเช่นนั้น! sqrt ดำเนินการเร็วเกินไปจนคุ้มค่ากับการดำเนินการแบบคู่ขนาน แม้ว่าจะวนซ้ำหลายครั้งก็ตาม สำหรับงานขนาดเล็ก ค่าใช้จ่ายในการจัดกำหนดการงานและการส่งคืนผลลัพธ์อาจมากกว่าเวลาในการดำเนินการงานเอง ส่งผลให้ประสิทธิภาพการทำงานไม่ดี นอกจากนี้ ตัวอย่างนี้ไม่ได้ใช้ความสามารถเวกเตอร์ของ sqrt ซึ่งจะต้องได้รับประสิทธิภาพที่เหมาะสม นี่เป็นเพียงการทดสอบและตัวอย่างการสอน ไม่ใช่เกณฑ์มาตรฐาน

ดังนั้นโดยธรรมชาติแล้วการตั้งค่าของคุณอาจไม่เร็วขึ้น

ให้ลองโดยไม่ขนานกัน แต่ใช้ vectorization:

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

มันทำเช่นเดียวกันกับลูปตัวอย่างของคุณทุกประการและเสร็จสิ้นในไม่กี่วินาที ทีนี้ลองสิ่งนี้ (มันยังคงทำสิ่งเดียวกันที่แน่นอนในเวลาเดียวกัน:

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

มันเพิ่มเป็น 1e6 1s a 1 ทันที (พลังของการทำให้เวกเตอร์ ... )

การรวมกันของ foreach กับ doParallel นั้นมาจากประสบการณ์ส่วนตัวของฉันช้ากว่าถ้าคุณใช้แพ็คเกจชีวสารสนเทศศาสตร์ BiocParallel จากที่เก็บ Bioconda (ฉันเป็นนักชีวสารสนเทศและในวิชาชีวสารสนเทศศาสตร์ เรามักจะมีสิ่งที่ต้องใช้การคำนวณหนักมาก เนื่องจากเรามีไฟล์ข้อมูลเดียวขนาดหลายกิกะไบต์ที่ต้องประมวลผล และหลายไฟล์ในนั้น) ฉันลองใช้ฟังก์ชันของคุณโดยใช้ BiocParallel และใช้ CPU ที่กำหนดทั้งหมด 100% (ทดสอบโดยการรัน htop ระหว่างการปฏิบัติงาน) ทั้งหมดใช้เวลา 17 วินาที

แน่นอน - ด้วยตัวอย่างแบบ Lightweight ของคุณ สิ่งนี้ใช้ได้กับ:

ค่าใช้จ่ายในการจัดกำหนดการงานและการส่งคืนผลลัพธ์อาจมากกว่าเวลาในการดำเนินการงานนั้นเอง

อย่างไรก็ตาม ดูเหมือนว่าจะใช้ CPU อย่างละเอียดมากกว่า 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. ดูที่ตัวนำชีวภาพว่ามีบรรจุภัณฑ์จำนวนเท่าใดและมีเอกสารประกอบอย่างดี ฉันหวังว่านี่จะช่วยคุณในเรื่องการประมวลผลแบบขนานในอนาคต

person Gwang-Jin Kim    schedule 11.10.2018
comment
ขอบคุณสำหรับคำแนะนำบางส่วน โค้ดจริงที่ฉันเรียกใช้แบบขนานนั้นเป็นการประมาณค่าขนาดใหญ่และไม่สามารถแปลงเป็นเวกเตอร์ได้อย่างแน่นอน ตัวอย่างของฉันแสดงให้เห็นอย่างชัดเจนว่า foreach ดูเหมือนว่าจะมีการประมวลผลล่วงหน้าซึ่งช้ามากสำหรับดัชนีขนาดใหญ่ มันไม่ได้เริ่มทำงานแบบคู่ขนานมาระยะหนึ่งแล้ว ฉันสามารถลองใช้แพ็คเกจนั้นได้เช่นกัน - person Rob Richmond; 12.10.2018
comment
@Rob Richmond: ใช่ ฉันรู้ คุณต้องการใช้มันสำหรับกระบวนการจริงซึ่งจะต้องใช้ทรัพยากรมากขึ้น ฉันแค่จะอธิบายว่าทำไมตัวอย่างพิเศษของคุณทำให้คุณผิดหวัง ใช่ ฉันคิดว่าด้วย BiocParallel คุณอาจจะพอใจมากขึ้น - person Gwang-Jin Kim; 12.10.2018
comment
ฉันเล่นกับ BiocParallel และพบว่ามันเป็นทางเลือกที่ยอดเยี่ยมสำหรับ foreach ดูเหมือนว่ามีบางอย่างเกี่ยวกับ foreach ที่ทำให้ต้องค้างกับงานจำนวนมากที่ยังไม่ได้รับคำตอบ อย่างไรก็ตาม ฉันจะยอมรับคำตอบของคุณเนื่องจากจะช่วยปรับปรุงขั้นตอนการทำงานโดยรวมของฉัน ขอบคุณ! - person Rob Richmond; 12.10.2018
comment
ยินดีต้อนรับ! ฉันดีใจที่สามารถช่วยได้! ใช่แล้ว ครั้งหนึ่งเมื่อต้นปีนี้ฉันได้ลองใช้ doParallel และ foreach ด้วย - และฉันก็ผิดหวังกับทั้งสองอย่างเช่นกัน ... ขอบคุณ! - person Gwang-Jin Kim; 13.10.2018