Cara mengoptimalkan skrip R saya untuk menggunakan "multicore"


15

Saya menggunakan GNU R di PC Ubuntu-Lucid yang memiliki 4 CPU. Untuk menggunakan semua 4 CPU, saya menginstal paket "r-cran-multicore". Karena manual paket tidak memiliki contoh praktis yang saya mengerti, saya perlu saran tentang cara mengoptimalkan skrip saya untuk menggunakan semua 4 CPU.

Dataset saya adalah data.frame (disebut P1) yang memiliki 50.000 baris dan 1600 cols. Untuk setiap baris, saya ingin menghitung maximun, jumlah dan mean. Script saya terlihat sebagai berikut:

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

Adakah yang bisa tolong beri tahu saya cara memodifikasi dan menjalankan skrip untuk menggunakan semua 4 CPU?


ada kesalahan dalam program di atas: baris seharusnya "for (i in 1: plength)"
Simon Byrne

Anda tegar, thx!
Produnis

1
bukankah ini milik StackOverflow?
R_Coholic

1
Ini termasuk dalam StackOverflow. Tidak ada pertanyaan statistik di sini sama sekali. Hanya pertanyaan pemrograman umum.
JD Long

Jawaban:


11

Menggunakan foreach dan doMC . Penjelasan terperinci dapat ditemukan di sini . Script Anda akan berubah sangat sedikit, garis

for(i in 1:plength){

harus diubah menjadi

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

Prasyarat untuk skrip multitasking yang menggunakan paket ini adalah

library(foreach)
library(doMC)
registerDoMC()

Catatan kehati-hatian. Menurut dokumentasi Anda tidak dapat menggunakan ini di GUI.

Sedangkan untuk masalah Anda, apakah Anda benar-benar membutuhkan multitasking? Data.frame Anda membutuhkan sekitar 1,2GB RAM, jadi itu harus sesuai dengan memori Anda. Jadi Anda cukup menggunakan apply:

p1smry <- apply(P1,1,summary)

Hasilnya akan menjadi matriks dengan ringkasan setiap baris.

Anda juga dapat menggunakan fungsi mclapply yang ada dalam paket multicore. Maka skrip Anda mungkin terlihat seperti ini:

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

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

Ini akan mengembalikan daftar, di mana elemen ke-i akan menjadi ringkasan dari baris ke-i. Anda dapat mengubahnya menjadi matriks menggunakan sapply

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

Terima kasih banyak. Anda benar, bahwa dengan "menerapkan" skrip dapat dioptimalkan. Saya hanya menggunakan skrip saya sebagai contoh minimal untuk mendapatkan pesan melalui ... Terima kasih banyak, jawaban Anda persis seperti yang saya cari !!
Produnis

15

Anda sudah mendapat jawaban tentang cara menggunakan lebih dari satu inti, tetapi masalah sebenarnya adalah dengan cara Anda menulis loop Anda. Jangan pernah memperluas vektor / objek hasil Anda di setiap iterasi dari satu loop . Jika Anda melakukan ini, Anda memaksa R untuk menyalin vektor hasil / objek Anda dan memperpanjangnya yang semuanya membutuhkan waktu. Alih-alih, pralokasi ruang penyimpanan yang cukup sebelum Anda memulai loop dan isi saat Anda melanjutkan. Berikut ini sebuah contoh:

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

Atau Anda dapat melakukan hal-hal ini melalui 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 

Tetapi perhatikan bahwa ini tidak lebih cepat daripada melakukan loop dengan benar dan kadang-kadang lebih lambat.

Namun, selalu waspada terhadap kode vektor. Anda dapat melakukan jumlah baris dan cara menggunakan rowSums()dan rowMeans()mana yang lebih cepat daripada loop atau applyversi:

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

   user  system elapsed 
  0.001   0.000   0.002 

Jika saya seorang pemain taruhan, saya akan mendapatkan uang untuk pendekatan ketiga yang saya sebut pemukulan foreach() atau opsi multi-core lainnya dalam tes kecepatan pada matriks Anda karena mereka harus mempercepat banyak hal untuk membenarkan biaya overhead yang dikeluarkan dalam mengatur proses terpisah yang bertani core CPU yang berbeda.

Pembaruan: Mengikuti komentar dari @shabbychef apakah lebih cepat untuk melakukan penjumlahan sekali dan menggunakan kembali dalam perhitungan mean?

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

   user  system elapsed 
  0.002   0.000   0.002

Tidak dalam uji coba ini, tetapi ini masih jauh dari lengkap ...


FWIW, Matlab memiliki masalah yang sama mengenai prealokasi dan perluasan vektor, dan merupakan kode klasik 'blooper'. Selain taruhan Anda, mungkin lebih cepat menggunakan hasil rowSumsuntuk menghitung rata-rata baris (kecuali jika saya melewatkan sesuatu mengenai misalnya Na atau NaN). Kode dalam pendekatan ketiga Anda menjumlahkan setiap kolom dua kali .
shabbychef

@shabbychef Anda akan terkejut (lihat jawaban saya yang diedit). Ya, penjumlahan dihitung secara acak dua kali, tetapi rowSumsdan rowMeanskode kompilasi yang sangat dioptimalkan dan apa yang kita peroleh hanya dengan menghitung penjumlahan sekali, kita kehilangan lagi dalam melakukan perhitungan rata-rata dalam kode yang ditafsirkan.
Reinstate Monica - G. Simpson

@ Gavin Simpson: tidak begitu cepat: coba sebagai gantinya system.time({ for (iii in c(1:1000)) { p1max3 <- apply(p1, 1, max) p1mean3 <- rowMeans(p1) p1sum3 <- rowSums(p1) } })dan serupa system.time({ for (iii in c(1:1000)) { p1max4 <- apply(p1, 1, max) p1sum4 <- rowSums(p1) p1mean4 <- p1sum4 / ncol(p1) } }); versi yang tidak menghitung ulang jumlah membutuhkan 1,368 detik di komputer saya; yang membutuhkan waktu 1,396. lagi, jauh dari lengkap, tetapi lebih menarik ...
shabbychef

@shabbychef kita harus memiliki ide yang berbeda tentang apa yang menarik atau tidak ;-) Faktanya, simulasi Anda yang lebih ketat memperkuat poin utama saya, bahwa rowMeansdan ketika rowSumsdiimplementasikan dalam kode kompilasi yang efisien dan dioptimalkan, mereka akan sulit dikalahkan.
Pasang kembali Monica - G. Simpson

@ Gavin Simpson. Sebenarnya, masalah dengan contoh saya adalah bahwa sebagian besar waktu diambil di bagian yang berlaku untuk menghitung maksimum. Saya setuju dengan Anda bahwa fungsi vectorized berbasis c seperti rowMeanakan sulit dikalahkan melalui alat R tujuan umum seperti *apply. Namun, Anda tampaknya menyarankan bahwa lebih cepat untuk menjumlahkan 10.000 angka dua kali melalui rowMeandan rowSumdaripada hanya sekali dan menggunakan operator divisi builtin R. Saya tahu R memiliki beberapa masalah efisiensi ( misalnya penemuan kurung kurawal vs masalah kurung), tapi itu sepertinya gila.
shabbychef

1

Silahkan lihat di salju dan hujan salju paket. Banyak contoh dengan ...

Jika Anda ingin mempercepat kode spesifik itu daripada belajar tentang R dan paralelisme, Anda harus melakukannya

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

tolong bantu saya untuk memodifikasi skrip saya ...
Produnis

2
Itu hanya menyembunyikan loop darimu. Masalah sebenarnya dengan kode @Produnis adalah bahwa penyalinan paksa sedang berlangsung karena vektor hasil diperpanjang pada setiap iterasi dari loop.
Pasang kembali Monica - G. Simpson

paket salju dapat memperluas solusi Gavin seperti mengatakan "kue". Paket memiliki banyak fungsi terapan yang dimodifikasi untuk melakukan multicoring. Untuk fungsi yang berlaku, Anda akan menggunakan sfApply (<dokumen Anda sebagaimana berlaku>). Salju juga didokumentasikan dengan baik. Saya harus menunjukkan bahwa tidak ada perangkat lunak tambahan yang diperlukan untuk melakukan ini pada prosesor multi-core. Lihat stackoverflow.com/questions/4164960/… untuk contoh sfLapply.
Roman Luštrik
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.