วิธีเพิ่มประสิทธิภาพสคริปต์ R ของฉันเพื่อใช้“ มัลติคอร์”


15

ฉันใช้ GNU R ที่ Ubuntu-Lucid PC ซึ่งมี 4 ซีพียู ในการใช้ CPU ทั้ง 4 ตัวฉันติดตั้งแพ็คเกจ "r-cran-multicore" เนื่องจากแพ็คเกจของคู่มือขาดตัวอย่างจริงที่ฉันเข้าใจฉันต้องการคำแนะนำในการเพิ่มประสิทธิภาพสคริปต์ของฉันเพื่อใช้งาน CPU ทั้ง 4 ตัว

ชุดข้อมูลของฉันคือ data.frame (เรียกว่า P1) ที่มี 50,000 แถวและ 1600 cols สำหรับแต่ละแถวฉันต้องการคำนวณค่าสูงสุดผลรวมและค่าเฉลี่ย สคริปต์ของฉันมีลักษณะดังนี้:

p1max <- 0
p1mean <- 0
p1sum <-0
plength <- length(P1[,1])
for(i in 1:plength){
   p1max <- c(p1max, max(P1[i,]))
   p1mean <- c(p1mean, mean(P1[i,]))
   p1sum <- c(p1sum, sum(P1[i,]))
}

ใครช่วยกรุณาบอกวิธีการปรับเปลี่ยนและเรียกใช้สคริปต์เพื่อใช้ซีพียูทั้ง 4 ตัว?


มีข้อผิดพลาดในโปรแกรมดังกล่าวข้างต้น: สายควรจะเป็น "สำหรับ (i 1: plength)"
ไซมอนเบิร์น

คุณมีความแข็งแกร่งขอบคุณ!
Produnis

1
สิ่งนี้ไม่ได้อยู่ใน StackOverflow หรือไม่
R_Coholic

1
สิ่งนี้ไม่ได้อยู่ใน StackOverflow ไม่มีคำถามสถิติเลย คำถามการเขียนโปรแกรมทั่วไปเท่านั้น
JD Long

คำตอบ:


11

ใช้foreachและdoMC คำอธิบายรายละเอียดสามารถพบได้ที่นี่ สคริปต์ของคุณจะเปลี่ยนบรรทัดน้อยมาก

for(i in 1:plength){

ควรเปลี่ยนเป็น

foreach(i=1:plength) %dopar% { 

สิ่งที่จำเป็นต้องมีสำหรับสคริปต์มัลติทาสก์ใด ๆ ที่ใช้แพ็กเกจเหล่านี้คือ

library(foreach)
library(doMC)
registerDoMC()

ข้อควรระวัง ตามเอกสารคุณไม่สามารถใช้สิ่งนี้ใน GUI

สำหรับปัญหาของคุณคุณต้องทำงานหลายอย่างพร้อมกันหรือไม่? data.frame ของคุณใช้หน่วยความจำ RAM ประมาณ 1.2GB ดังนั้นควรจะพอดีกับหน่วยความจำของคุณ ดังนั้นคุณสามารถใช้:

p1smry <- apply(P1,1,summary)

ผลลัพธ์จะเป็นเมทริกซ์ที่มีบทสรุปของแต่ละแถว

คุณยังสามารถใช้ฟังก์ชันmclapplyซึ่งอยู่ในแพ็คเกจมัลติคอร์ จากนั้นสคริปต์ของคุณอาจมีลักษณะเช่นนี้:

loopfun <- function(i) {
     summary(P1[i,])
}

res <- mclapply(1:nrow(P1),loopfun)

นี่จะส่งคืนรายการโดยที่องค์ประกอบ i-th จะเป็นข้อมูลสรุปของแถวที่ i คุณสามารถแปลงเป็นเมทริกซ์โดยใช้อย่างราบรื่น

mres <- sapply(res,function(x)x)

ขอบคุณมาก. คุณพูดถูกว่าด้วย "นำไปใช้" สคริปต์อาจถูกปรับให้เหมาะสม ฉันเพิ่งใช้สคริปต์ของฉันเป็นตัวอย่างเล็กน้อยเพื่อให้ได้รับข้อความผ่าน ... ขอบคุณมากคำตอบของคุณคือสิ่งที่ฉันกำลังมองหา !!
Produnis

15

คุณได้รับคำตอบเกี่ยวกับวิธีใช้มากกว่าหนึ่งคอร์ แต่ปัญหาที่แท้จริงคือวิธีที่คุณเขียนลูป ไม่เคยขยายผลของเวกเตอร์ / วัตถุที่ทวนของวงแต่ละคน หากคุณทำเช่นนี้คุณบังคับให้ R คัดลอกผลเวกเตอร์ / วัตถุของคุณและขยายเวลาที่ใช้เวลาทั้งหมด ให้จัดสรรพื้นที่เก็บข้อมูลให้เพียงพอก่อนที่คุณจะเริ่มวนรอบและเติมตามที่คุณต้องการ นี่คือตัวอย่าง:

set.seed(1)
p1 <- matrix(rnorm(10000), ncol=100)
system.time({
p1max <- p1mean <- p1sum <- numeric(length = 100)
for(i in seq_along(p1max)){
   p1max[i] <- max(p1[i,])
   p1mean[i] <- mean(p1[i,])
   p1sum[i ]<- sum(p1[i,])
}
})

   user  system elapsed 
  0.005   0.000   0.005

หรือคุณสามารถทำสิ่งเหล่านี้ผ่านapply():

system.time({
p1max2 <- apply(p1, 1, max)
p1mean2 <- apply(p1, 1, mean)
p1sum2 <- apply(p1, 1, sum)
})
   user  system elapsed 
  0.007   0.000   0.006 

แต่โปรดทราบว่านี่ไม่เร็วไปกว่าการวนซ้ำอย่างถูกต้องและช้าลง

อย่างไรก็ตามควรมองหารหัส vectorised เสมอ คุณสามารถทำผลรวมแถวและวิธีการใช้rowSums()และrowMeans()ซึ่งเร็วกว่าการวนซ้ำหรือapplyรุ่น:

system.time({
p1max3 <- apply(p1, 1, max)
p1mean3 <- rowMeans(p1)
p1sum3 <- rowSums(p1)
})

   user  system elapsed 
  0.001   0.000   0.002 

ถ้าฉันเป็นนักพนันฉันจะมีเงินในวิธีที่สามที่ฉันพูดถึงการตีforeach()หรือตัวเลือกแบบมัลติคอร์อื่น ๆ ในการทดสอบความเร็วในเมทริกซ์ของคุณเพราะพวกเขาจะต้องเพิ่มความเร็วให้มากขึ้นเพื่อปรับค่าใช้จ่าย กระบวนการแยกที่ทำหน้าที่แกน CPU ที่แตกต่างกัน

อัปเดต: การติดตามความคิดเห็นจาก @shabbychef เร็วกว่าที่จะทำผลรวมครั้งเดียวและนำมาใช้ใหม่ในการคำนวณค่าเฉลี่ยหรือไม่

system.time({
    p1max4 <- apply(p1, 1, max)
    p1sum4 <- rowSums(p1)
    p1mean4 <- p1sum4 / ncol(p1)
    })

   user  system elapsed 
  0.002   0.000   0.002

ไม่ได้อยู่ในการทดสอบนี้ แต่มันยังห่างไกลจากความครบถ้วนสมบูรณ์ ...


FWIW, Matlab มีปัญหาเดียวกันเกี่ยวกับการจัดสรรล่วงหน้าและการขยายเวกเตอร์และเป็นรหัสคลาสสิก 'blooper' นอกเหนือจากการเดิมพันของคุณแล้วมันอาจจะเร็วกว่าที่จะใช้ผลลัพธ์ของrowSumsการคำนวณแถวหมายความว่า (เว้นแต่ฉันจะพลาดอะไรบางอย่างเช่น Na หรือ NaN) รหัสในแนวทางที่สามของคุณสรุปแต่ละคอลัมน์เป็นครั้งที่สอง
shabbychef

@shabbychef คุณจะประหลาดใจ (ดูคำตอบที่แก้ไขของฉัน) ใช่ผลรวมจะได้รับการคำนวณอย่างระมัดระวังสองครั้ง แต่rowSumsและrowMeansเป็นรหัสที่ได้รับการปรับปรุงให้ดีที่สุดและสิ่งที่เราได้รับจากการคำนวณจำนวนเงินครั้งเดียวเราจะทำการคำนวณค่าเฉลี่ยในรหัสตีความอีกครั้ง
Reinstate Monica - G. Simpson

@ กาวินซิมป์สัน: ไม่เร็ว: ลองแทนsystem.time({ for (iii in c(1:1000)) { p1max3 <- apply(p1, 1, max) p1mean3 <- rowMeans(p1) p1sum3 <- rowSums(p1) } })และในทำนองเดียวกันsystem.time({ for (iii in c(1:1000)) { p1max4 <- apply(p1, 1, max) p1sum4 <- rowSums(p1) p1mean4 <- p1sum4 / ncol(p1) } }); รุ่นที่ไม่สามารถคำนวณผลรวมได้ใช้เวลา 1.368 วินาทีในคอมพิวเตอร์ของฉัน อันที่ 1.396 อีกห่างไกลจากหมดจด แต่น่าสนใจมากขึ้น ...
shabbychef

@ shabbychef เราต้องมีความคิดที่แตกต่างกันในสิ่งที่เป็นหรือไม่น่าสนใจ ;-) ที่จริงแล้วการจำลองที่เข้มงวดยิ่งขึ้นของคุณช่วยเสริมจุดหลักของฉันนั่นคือrowMeansและrowSumsถูกนำไปใช้ในการคอมไพล์ที่มีประสิทธิภาพ
Reinstate Monica - G. Simpson

@ กาวินซิมป์สัน ที่จริงปัญหากับตัวอย่างของฉันคือส่วนใหญ่ใช้เวลาในส่วนนำไปใช้ในการคำนวณสูงสุด ผมเห็นด้วยกับคุณว่า C-ตามฟังก์ชั่น vectorized เช่นrowMeanเป็นไปได้ยากที่จะชนะผ่านเครื่องมือ R *applyวัตถุประสงค์ทั่วไปเช่น อย่างไรก็ตามคุณดูเหมือนจะแนะนำว่ามันเร็วกว่าที่จะรวม 10,000 หมายเลขสองครั้งผ่านทางrowMeanและrowSumมากกว่าเพียงครั้งเดียวและใช้ตัวดำเนินการแบ่ง R ของ builtin ฉันรู้ว่า R มีปัญหาด้านประสิทธิภาพบางอย่าง ( เช่นการค้นพบเมื่อเร็ว ๆ นี้เกี่ยวกับเครื่องหมายปีกกาเทียบกับปัญหาวงเล็บ) แต่ดูเหมือนว่าบ้า
shabbychef

1

มีลักษณะที่เป็นหิมะและหิมะแพคเกจ ตัวอย่างมากมายกับสิ่งเหล่านั้น ...

หากคุณต้องการเพิ่มความเร็วรหัสเฉพาะแทนที่จะเรียนรู้เกี่ยวกับ R และความขนานคุณควรทำสิ่งนี้

P1 = matrix(rnorm(1000), ncol=10, nrow=10
apply(P1, 1, max)
apply(P1, 1, mean)
apply(P1, 1, sum)

โปรดช่วยฉันแก้ไขสคริปต์ของฉันด้วย ...
Produnis

2
นี่เป็นเพียงการซ่อนวงจากคุณ ปัญหาที่แท้จริงของรหัส @Produnis คือการคัดลอกแบบบังคับเกิดขึ้นเนื่องจากเวกเตอร์ผลลัพธ์ถูกขยายในแต่ละการวนซ้ำของลูป
Reinstate Monica - G. Simpson

แพ็คเกจหิมะสามารถขยายโซลูชันของกาวินเช่นการพูดว่า "เค้ก" แพคเกจมีฟังก์ชั่นการปรับใช้มากมายให้ทำ สำหรับฟังก์ชั่นการใช้งานคุณจะใช้ sfApply (<yourarguments เป็น for Apply>) มีการบันทึกปริมาณหิมะไว้ด้วย ฉันควรชี้ให้เห็นว่าไม่จำเป็นต้องมีซอฟต์แวร์พิเศษสำหรับการดำเนินการนี้ในโปรเซสเซอร์แบบมัลติคอร์ ดูstackoverflow.com/questions/4164960/…สำหรับตัวอย่าง sfLapply
Roman Luštrik
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.