Cách tối ưu hóa tập lệnh R của tôi để sử dụng đa phương tiện


15

Tôi đang sử dụng GNU R tại PC Ubuntu-Lucid có 4 CPU. Để sử dụng tất cả 4 CPU, tôi đã cài đặt gói "r-cran-multicore". Vì hướng dẫn sử dụng gói thiếu các ví dụ thực tế mà tôi hiểu, tôi cần lời khuyên về cách tối ưu hóa tập lệnh của mình để sử dụng cả 4 CPU.

Tập dữ liệu của tôi là data.frame (được gọi là P1) có 50.000 hàng và 1600 cols. Đối với mỗi hàng, tôi muốn tính maximun, sum và mean. Kịch bản của tôi trông như sau:

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,]))
}

Ai đó có thể vui lòng cho tôi biết cách sửa đổi và chạy tập lệnh để sử dụng cả 4 CPU không?


có một lỗi trong chương trình trên: dòng nên là "for (i in 1: plength)"
Simon Byrne

bạn thật cứng nhắc, thx!
Produnis

1
cái này không thuộc về StackOverflow sao?
R_Coholic

1
Điều này không thuộc về StackOverflow. Không có câu hỏi thống kê nào ở đây cả. Chỉ có một câu hỏi lập trình chung.
JD Long

Câu trả lời:


11

Sử dụng foreachdoMC . Giải thích chi tiết có thể được tìm thấy ở đây . Kịch bản của bạn sẽ thay đổi rất ít, dòng

for(i in 1:plength){

nên được đổi thành

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

Điều kiện tiên quyết cho mọi tập lệnh đa nhiệm sử dụng các gói này là

library(foreach)
library(doMC)
registerDoMC()

Lưu ý thận trọng. Theo tài liệu bạn không thể sử dụng trong GUI.

Đối với vấn đề của bạn, bạn có thực sự cần đa nhiệm? Data.frame của bạn cần khoảng 1,2 GB RAM, vì vậy nó sẽ phù hợp với bộ nhớ của bạn. Vì vậy, bạn chỉ có thể sử dụng áp dụng:

p1smry <- apply(P1,1,summary)

Kết quả sẽ là một ma trận với các bản tóm tắt của mỗi hàng.

Bạn cũng có thể sử dụng hàm mclapply trong gói đa lõi. Sau đó, kịch bản của bạn có thể trông như thế này:

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

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

Điều này sẽ trả về danh sách, trong đó phần tử thứ i sẽ là tóm tắt của hàng thứ i. Bạn có thể chuyển đổi nó thành ma trận bằng cách sử dụng sapply

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

Cảm ơn rât nhiều. Bạn đã đúng, rằng với "áp dụng", kịch bản có thể được tối ưu hóa. Tôi chỉ sử dụng kịch bản của mình như một ví dụ tối thiểu để có được thông điệp qua ... Thx rất nhiều, câu trả lời của bạn chính xác là những gì tôi đang tìm kiếm !!
Produnis

15

Bạn đã có câu trả lời về cách sử dụng nhiều hơn một lõi, nhưng vấn đề thực sự nằm ở cách bạn viết các vòng lặp. Không bao giờ mở rộng vectơ kết quả / đối tượng của bạn tại mỗi lần lặp của một vòng lặp . Nếu bạn làm điều này, bạn buộc R sao chép vectơ / đối tượng kết quả của bạn và mở rộng nó mà tất cả đều mất thời gian. Thay vào đó, hãy phân bổ đủ không gian lưu trữ trước khi bạn bắt đầu vòng lặp và điền vào khi bạn đi cùng. Đây là một ví dụ:

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

Hoặc bạn có thể làm những điều này thông qua 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 

Nhưng lưu ý rằng đây là không nhanh hơn làm vòng lặp đúng và đôi khi chậm hơn.

Tuy nhiên, luôn luôn đề phòng mã vector. Bạn có thể thực hiện tổng hàng và phương tiện bằng cách sử dụng rowSums()rowMeans()nhanh hơn so với vòng lặp hoặc applyphiên bản:

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

   user  system elapsed 
  0.001   0.000   0.002 

Nếu tôi là người cá cược, tôi sẽ có tiền cho cách tiếp cận thứ ba mà tôi đề cập đến việc đánh bại foreach()hoặc các tùy chọn đa lõi khác trong bài kiểm tra tốc độ trên ma trận của bạn bởi vì họ sẽ phải tăng tốc đáng kể để biện minh cho chi phí phát sinh trong việc thiết lập các quy trình riêng biệt được trang bị các lõi CPU khác nhau.

Cập nhật: Theo nhận xét từ @shabbychef, có nhanh hơn để thực hiện các khoản tiền một lần và sử dụng lại trong tính toán của giá trị trung bình không?

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

   user  system elapsed 
  0.002   0.000   0.002

Không phải trong lần chạy thử này, nhưng điều này còn lâu mới ...


FWIW, Matlab có cùng các vấn đề liên quan đến việc sắp xếp và mở rộng các vectơ, và là một mã cổ điển 'blooper'. Ngoài cược của bạn, có thể sử dụng kết quả rowSumsđể tính toán hàng có nghĩa là nhanh hơn (trừ khi tôi thiếu một cái gì đó liên quan đến ví dụ Na hoặc NaN). Mã trong cách tiếp cận thứ ba của bạn tính tổng mỗi cột hai lần .
shabbychef

@shabbychef bạn sẽ ngạc nhiên (xem câu trả lời đã được chỉnh sửa của tôi). Có Tổng số tiền được notionally tính hai lần, nhưng rowSumsrowMeansđược đánh giá cao được tối ưu hóa mã biên dịch và những gì chúng ta đạt được trong chỉ tính số tiền một lần, chúng tôi mất một lần nữa trong việc thực hiện tính toán trung bình trong mã giải thích.
Phục hồi Monica - G. Simpson

@Gavin Simpson: không quá nhanh: hãy thử thay thế system.time({ for (iii in c(1:1000)) { p1max3 <- apply(p1, 1, max) p1mean3 <- rowMeans(p1) p1sum3 <- rowSums(p1) } })và tương tự system.time({ for (iii in c(1:1000)) { p1max4 <- apply(p1, 1, max) p1sum4 <- rowSums(p1) p1mean4 <- p1sum4 / ncol(p1) } }); phiên bản không tính toán lại tổng số mất 1,368 giây trên máy tính của tôi; một trong đó mất 1.394. một lần nữa, xa hoàn toàn, nhưng hấp dẫn hơn ...
shabbychef

@shabbychef chúng ta phải có ý kiến khác nhau về những gì là hay là không hấp dẫn ;-) Trong thực tế, mô phỏng chặt chẽ hơn của bạn củng cố điểm chính của tôi, đó là rowMeansrowSumsđược thực hiện trong hiệu quả, tối ưu hóa mã biên dịch họ sẽ rất khó để đánh bại.
Phục hồi Monica - G. Simpson

@Gavin Simpson. Trên thực tế, vấn đề với ví dụ của tôi là hầu hết thời gian được dành cho phần áp dụng để tính toán tối đa. Tôi đồng ý với bạn rằng một hàm vectơ dựa trên c rowMeansẽ khó bị đánh bại thông qua một công cụ R có mục đích chung như thế nào *apply. Tuy nhiên, bạn dường như cho thấy rằng nó là nhanh hơn để tổng hợp 10000 số hai lần thông qua rowMeanrowSumthay vì chỉ một lần và điều hành bộ phận được xây dựng trong sử dụng R. Tôi biết R có một số vấn đề hiệu quả ( ví dụ như phát hiện gần đây về dấu ngoặc nhọn so với vấn đề dấu ngoặc đơn), nhưng điều đó có vẻ điên rồ.
shabbychef

1

Có một cái nhìn vào các gói tuyếttuyết rơi . Rất nhiều ví dụ với những ...

Nếu bạn muốn tăng tốc mã cụ thể đó thay vì tìm hiểu về R và song song, bạn nên làm điều này

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

vui lòng giúp tôi sửa đổi kịch bản của mình ...
Produnis

2
Những người chỉ đang che giấu vòng lặp từ bạn. Vấn đề thực sự với mã @Produnis là việc sao chép bắt buộc đang diễn ra do các vectơ kết quả đang được mở rộng ở mỗi lần lặp của vòng lặp.
Phục hồi Monica - G. Simpson

gói tuyết rơi có thể mở rộng giải pháp của Gavin như nói "bánh". Gói có rất nhiều chức năng áp dụng được sửa đổi để thực hiện đa hướng. Đối với chức năng áp dụng, bạn sẽ sử dụng sfApply (<yourargument như áp dụng>). Tuyết rơi cũng được ghi chép lại. Tôi nên chỉ ra rằng không cần phần mềm bổ sung để thực hiện việc này trên bộ xử lý đa lõi. Xem stackoverflow.com/questions/4164960/ cho ví dụ sfLapply.
Roman Luštrik
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.