Cara mempercepat kerja dengan R-language API menggunakan komputasi paralel, menggunakan contoh Yandex.Direct API (Bagian 2)

Pada artikel terakhir , saya berbicara tentang apa itu multithreading dan memberikan contoh penerapannya dalam bahasa R ketika bekerja dengan Yandex.Direct API menggunakan doSNOW , doParallel dan konstruksi foreach .


Artikel ini merupakan kelanjutan, tetapi dapat dianggap sebagai panduan offline untuk multithreading di R. Saya diminta untuk menulisnya dengan komentar yang diterima di bagian pertama (di sini terima kasih khusus kepada Alexey_mosc , SatCat , Ananiev_Genrih ), di mana saya diberi sejumlah paket yang mewakili pendekatan yang lebih modern untuk implementasi multithreading di R, kita akan membicarakannya nanti.


Multithreading


Isi



Tantangan


Sebagai contoh, kami mengambil masalah yang dipertimbangkan dalam publikasi sebelumnya , yaitu dalam mode multi-utas, kumpulkan daftar kata kunci dari 4 akun iklan Yandex.Direct.


Untuk bekerja dengan Yandex.Direct API, kami akan menggunakan paket ryandexdirect . Dokumentasi resmi untuk itu ada di tautan , tetapi untuk implementasi tugas yang dijelaskan, kita hanya perlu 2 fungsi:


  • yadirAuth - otorisasi di Yandex.Direct API;
  • yadirGetKeyWords - Unduh daftar kata kunci dari akun iklan.

Bukan saja saya memilih proses mengunduh kata kunci, faktanya adalah ini adalah salah satu operasi yang paling panjang di Yandex.Direct API. Kedua, di semua akun jumlah kata kunci berbeda, oleh karena itu, waktu untuk menyelesaikan operasi ini untuk setiap akun akan sangat berbeda, dalam kasus kami dari 1 hingga 20 detik.


Persiapan


Awalnya, Anda perlu menginstal semua paket yang dibahas dalam artikel ini, untuk ini Anda dapat menggunakan kode di bawah ini.


Kode 1: Instalasi Paket
 #    install.packages("ryandexdirect") install.packages("tictoc") install.packages("rbenchmark") install.packages("dplyr") install.packages("purrr") install.packages("future") install.packages("promises") install.packages("furrr") install.packages("future.apply") 

Agar fungsi-fungsi paket tersedia untuk Anda, Anda harus menghubungkannya menggunakan perintah library . Untuk kenyamanan, saya akan secara terpisah menghubungkan semua paket yang diperlukan dalam setiap contoh kode yang diberikan.


Kami membuat vektor yang terdiri dari login Yandex.Direct, dari mana nanti kami akan meminta kata kunci:


Kode 2: Membuat Vektor Login
 logins <- c("login1", "login2", "login3", "login4") 

Untuk bekerja dengan Yandex.Direct API, Anda harus terlebih dahulu melalui otorisasi di bawah setiap akun, untuk ini Anda dapat menggunakan desain berikut:


Kode 3: Otorisasi di Yandex.Direct API
 lapply(logins, function(l) { yadirAuth(Login = l)}) 

Setelah menjalankan kode di atas, browser akan terbuka untuk otorisasi di bawah setiap akun. Anda mengonfirmasi izin untuk ryandexdirect untuk mengakses materi iklan Anda. Anda akan diarahkan ke halaman tempat Anda perlu menyalin kode verifikasi. Dengan memasukkannya ke konsol R, selesaikan proses otorisasi. Operasi ini diulangi untuk setiap login yang Anda tentukan saat membuat login vektor.


Beberapa pengguna, selama proses otorisasi, mungkin bingung dengan fakta pengalihan ke sumber daya pihak ketiga, tetapi tidak ada bahaya pada akun Anda dalam hal ini, saya menjelaskan topik ini secara lebih rinci dalam artikel "Seberapa Aman Menggunakan Paket R untuk Bekerja dengan API Sistem Periklanan" .


Selanjutnya, kami akan mempertimbangkan beberapa contoh implementasi tugas yang dijelaskan. Masing-masing akan dimulai dengan kode contoh, dan penjelasan selanjutnya. Saya pikir opsi ini akan paling nyaman untuk persepsi.


Contoh solusi pemrosesan serial, fungsi sapply dan paket purrr



Pada artikel terakhir , saya mengutip solusi menggunakan for sebagai contoh. Karena kami menganggap multithreading menggunakan paket foreach , yang sintaksisnya menyerupai loop, contoh ini sesuai di sana, meskipun penggunaan loop tidak disambut oleh pengguna R.


Paket-paket yang akan kita bahas dalam artikel ini lebih mengingatkan pada fungsi-fungsi keluarga yang berlaku dalam sintaksis, oleh karena itu, saya akan memberikan contoh solusi dalam mode serial menggunakannya.


Fungsi sapply


Untuk memperkirakan waktu eksekusi perintah, dalam setiap pendekatan yang dipertimbangkan, kami akan menggunakan paket tictoc .

Kode 4: Contoh solusi dalam mode berurutan menggunakan fungsi sapply
 library(tictoc) library(dplyr) tic() #   kw.sapply <- sapply( logins, #  ,     function(x) #        #     { yadirGetKeyWords(Login = x) %>% mutate(login = x) }, simplify = FALSE #     ) toc() #   #       result.sapply <- do.call("rbind", kw.sapply) 

Waktu 39.36 sec elapsed : 39.36 sec elapsed


Pada awalnya, sintaks fungsi dari keluarga yang apply tidak semudah dibaca sebagai sintaks dari loop, tetapi pada kenyataannya semuanya cukup sederhana.


sapply(X, FUN)


Dimana:


  • X - Objek yang elemen-elemennya akan kita iterate dan gunakan secara bergantian pada setiap iterasi, dalam for loop terlihat seperti ini: for(i in X) ;
  • FUN - Fungsi di mana kita akan mengganti setiap elemen dari objek X secara bergantian, jika kita menggambar analogi dengan for , maka ini adalah tubuh loop.

Dalam Contoh Kode 4 , vektor login yang dibuat sebelumnya diteruskan ke argumen X. Setiap elemen vektor login diteruskan sebagai satu-satunya argumen ke fungsi function(x) { yadirGetKeyWords(Login = x) %>% mutate(login = x) } anonim function(x) { yadirGetKeyWords(Login = x) %>% mutate(login = x) } yang diteruskan ke argumen FUN .


Yaitu sapply akan menjalankan fungsi yang ditentukan dalam FUN 4 kali, mengganti loginnya satu per satu, dan mengembalikan hasilnya dalam bentuk daftar (objek daftar kelas) yang terdiri dari 4 elemen. Setiap elemen adalah tabel dengan daftar kata kunci yang diterima dari akun pada setiap iterasi.


  1. yadirGetKeyWords(Login = "login1") %>% mutate(login = "login1")
  2. yadirGetKeyWords(Login = "login2") %>% mutate(login = "login2")
  3. yadirGetKeyWords(Login = "login3") %>% mutate(login = "login3")
  4. yadirGetKeyWords(Login = "login4") %>% mutate(login = "login4")

Objek yang diperoleh menggunakan sapply memiliki struktur sebagai berikut:


 summary(kw.sapply) 

  Length Class Mode login1 19 data.frame list login2 19 data.frame list login3 19 data.frame list login4 19 data.frame list 

Pada akhir contoh ini, perintah result.sapply <- do.call("rbind", kw.sapply) menggabungkan semua 4 elemen dari daftar kw.sapply ke dalam satu frame result.sapply .


 # A tibble: 6,804 x 1 result.sapply$Id $Keyword $AdGroupId $CampaignId $ServingStatus $State <dbl> <fct> <dbl> <int> <fct> <fct> 1 15164230566 ~ 3597453985 39351725 ELIGIBLE ON 2 15164230567  ~ 3597453985 39351725 ELIGIBLE ON 3 15164230568  ~ 3597453985 39351725 ELIGIBLE ON 4 15164230569 ~ 3597453985 39351725 ELIGIBLE ON 5 15164230570 ~ 3597453985 39351725 ELIGIBLE ON 6 15164230571  ~ 3597453985 39351725 ELIGIBLE ON 7 15164230572 ~ 3597453985 39351725 ELIGIBLE ON 8 15164230573  ~ 3597453985 39351725 ELIGIBLE ON 9 15164230574 ~ 3597453985 39351725 ELIGIBLE ON 10 15164230575 ~ 3597453985 39351725 ELIGIBLE ON # ... with 6,794 more rows, and 13 more variables: $Status <fct>, # $StrategyPriority <fct>, $StatisticsSearchImpressions <int>, # $StatisticsSearchClicks <int>, $StatisticsNetworkImpressions <int>, # $StatisticsNetworkClicks <lgl>, $UserParam1 <chr>, $UserParam2 <chr>, # $ProductivityValue <lgl>, $ProductivityReferences <lgl>, $Bid <dbl>, # $ContextBid <dbl>, $login <chr> 

Selain sapply , keluarga *apply fungsi meliputi: apply , lapply , vapply , mapply , dan lainnya.


Paket purrr


Kode 5: Contoh solusi menggunakan fungsi paket purrr
 library(purrr) library(dplyr) library(tictoc) tic() #   result.purrr <- map_df( logins, #  ,     ~ #   function(.x) { yadirGetKeyWords(Login = .x) %>% mutate(login = .x) } ) toc() #   

Waktu 35.46 sec elapsed : 35.46 sec elapsed


Paket purrr adalah bagian dari inti dari perpustakaan tidyverse , ditulis oleh Headley Wickham.


Dalam hal makna dan sintaksis, fungsi utama dari paket sangat mirip dengan sapply , keuntungan utamanya adalah sebagai berikut:


  • Fungsi dibagi menjadi map keluarga, map2 , pmap , walk dan sebagainya, fungsi terpisah yang termasuk dalam keluarga yang sama mengembalikan hasilnya dalam format yang berbeda: chr , dbl , int , df , dll;
  • Fungsi keluarga map2 Anda untuk map2 elemen (iterate) secara bersamaan dari dua objek;
  • Fungsi keluarga pmap Anda untuk secara bersamaan pmap ke elemen dari sejumlah objek. Anda dapat mengirimkan tabel ke input ke argumen .l (analog dari argumen X dengan sapply) , yang masing-masing kolomnya akan berisi nilai-nilai yang Anda gunakan untuk mengulangi, dan yang nantinya akan diganti menjadi argumen dengan fungsi yang sama dengan yang dilewatkan .f (analog FUN dari sapply) .

Dalam situasi apa kita perlu mengulangi elemen dari beberapa objek. Misalnya, Anda bekerja dengan beberapa akun agen, dan akun iklan tempat Anda ingin mendapatkan daftar kata kunci tersebar di antara mereka. Dalam hal ini, Anda dapat membuat vektor dari nama akun agen, dan beralih di atasnya, bersamaan dengan cara Anda menyortir login akun iklan.


Kode 6: Contoh bekerja dengan banyak akun agen
 library(purrr) #      agencies <- c("agency1", NA, "agency2", "agency1") #      #         result.pmap2 <- map2_df(.x = logins, .y = agencies, ~ { yadirGetKeyWords(Login = .x, AgencyAccount = .y) %>% mutate(login = .x) }) 

Sekarang bayangkan situasi ketika Anda login di bawah akun yang berbeda, Anda menyimpan file dengan kredensial di folder yang berbeda, maka Anda harus segera beralih pada tiga objek: login akun iklan, login akun agen, jalur di mana file dengan kredensial disimpan. Ini bisa dilakukan dengan bantuan. pmap keluarga pmap .


Kode 7: contoh fungsi pmap
 library(purrr) #  ,       #      TokenPath <- c("C:\\proj1\\tokens", "C:\\yandex\\token", "C:\\yandex\\token", "C:\\my_yandex_acoount") #   pmap.result <- pmap_df(list(Login = logins, AgencyAccount = agencies, TokenPath = TokenPath), yadirGetKeyWords) 

Dengan demikian, hasil dari mengeksekusi fungsi map_df , map2_df dan pmap_df adalah bingkai tanggal, dan ketika menggunakannya, langkah terakhir dari contoh dengan sapply ( do.call("rbind", kw.sapply) ) tidak diperlukan.


Kode tersebut menjadi lebih ringkas dan dieksekusi sedikit lebih cepat, namun demikian, baik pendekatan yang diuraikan, sapply dan purrr , mengumpulkan kata kunci dari setiap akun secara berurutan. Oleh karena itu, total waktu pelaksanaan operasi ini sama dengan jumlah durasi pengumpulan data dari keempat akun.


Waktu [total] = Waktu [login1] + Waktu [login2] + Waktu [login3] + Waktu [login4]


Opsi multithreaded untuk menyelesaikan tugas mengumpulkan kata kunci dari Yandex.Direct



Jadi, jika Anda sudah membaca artikel pertama , maka Anda tahu bahwa mode operasi multithreaded memiliki beberapa fitur:


  • Setiap utas dimulai dalam sesi R yang terpisah dengan lingkungan kerja yang bersih.
  • Untuk alasan yang sama, dalam proses berjalan yang terpisah, paket yang terhubung sebelumnya tidak dikirimkan secara default.

Mengekspor objek yang dibuat dalam lingkungan kerja, dan menghubungkan paket dalam setiap pendekatan diimplementasikan secara berbeda, maka kami akan mempertimbangkannya secara lebih rinci.


Paket parallel


Paket ini pertama kali dimasukkan dalam paket R dalam versi 2.14.0 dan hingga hari ini hadir dengan R itu sendiri.


Kode 8: Contoh solusi untuk masalah melalui paket paralel
 library(parallel) library(tictoc) #   cl <- makeCluster(4) #      clusterExport(cl = cl, varlist = "logins") #  ,      #  ,       ryandexdirect clusterEvalQ(cl = cl, { library(ryandexdirect) library(dplyr) } ) tic() #   parallel.kw <- parSapplyLB(cl = cl, #   X = logins, # ,     FUN = function(x) { #      #      X yadirGetKeyWords(Login = x) %>% mutate(login = x) }, simplify = F) #    toc() #     #   stopCluster(cl) #      result.parallel <- dplyr::bind_rows(parallel.kw) 

16.75 sec elapsed Time: 16.75 sec elapsed


Mari kita coba menguraikan Kode 8 . Fungsi makeCluster membuat sekelompok 4 proses. Kita dapat mengekspor objek dari lingkungan kerja utama kita ke cluster yang dibuat menggunakan fungsi clusterExport , untuk ini kita perlu menggunakan argumennya:


  • cl - Cluster dimana kita akan mengekspor objek
  • varlist - Vektor teks yang berisi nama-nama objek yang akan diekspor ke setiap proses cluster.

Salah satu cara untuk menghubungkan paket yang tepat pada setiap node cluster adalah dengan menggunakan fungsi clusterEvalQ . Dalam contoh kami, kami menggunakannya untuk menghubungkan paket, tetapi Anda dapat menulis kode R apa pun di dalam clusterEvalQ , dan itu akan diluncurkan pada awal setiap node cluster. Argumen untuk fungsi ini cukup jelas, Anda perlu menentukan cluster dan perintah yang akan dieksekusi di dalamnya.


parSapplyLB adalah versi paralel dari fungsi sapply dengan load balancing antara node cluster, mereka juga menggunakannya, tetapi Anda perlu menentukan cluster dengan argumen cl .


Juga secara parallel ada versi paralel lain dari *apply fungsi keluarga: parLapply , parSapply , parApply , dll.


parSapply berbeda dari parSapplyLB hanya karena parSapplyLB tidak memiliki load balancing pada node cluster.


Fungsi stopCluster digunakan untuk menghentikan cluster yang dibuat.


Perintah terakhir, dplyr::bind_rows(parallel.kw) kami menggabungkan objek parallel.kw yang diperoleh dengan menggunakan parSapplyLB menjadi satu tabel.


Untuk Linux, parallel memiliki fungsi terpisah: mclapply , mcmapply , mcMap . Seringkali dalam sistem operasi ini, perintah dieksekusi lebih cepat, dan kode menjadi lebih kompak.


Kode 9: Solusi menggunakan mclapply untuk Linux
 library(parallel) library(tictic) library(dplyr) library(ryandexdirect) tic() mclapply.kw <- mclapply(logins, FUN = function(x) { #      #      X yadirGetKeyWords(Login = x) %>% mutate(login = x) }, mc.cores = 4) toc() 

Saat menggunakan fungsi-fungsi ini, tidak perlu memulai cluster menggunakan makeCluster . jumlah node yang Anda tentukan menggunakan argumen mc.cores . Juga tidak perlu menghubungkan paket dan mengekspor objek, operasi ini dilakukan secara otomatis.


Paket future


Salah satu pendekatan paling modern untuk pemrograman asinkron dalam R.


Kode yang secara paralel akan menyelesaikan masalah kita dengan bantuan future cukup rumit untuk dipahami. Karena itu, mari kita menganalisis kerjanya pada contoh yang lebih sederhana, kami akan meminta daftar kata kunci dari satu akun.


Kode 10: Contoh paling sederhana untuk menggunakan paket yang akan datang
 library(future) #    plan(multiprocess) #      #    future.kw <- future({yadirGetKeyWords(Login = logins[4])}, packages = "ryandexdirect", globals = "logins") #     resolved(future.kw) #     future.result.1 <- value(future.kw) 

Mari kita coba mencari tahu contoh kode 10 . Fungsi plan memungkinkan Anda untuk mengatur dan mengubah mode eksekusi dari ekspresi yang diberikan, berikut adalah yang utama:


  • sequential - Ini adalah mode operasi R yang biasa; perintah dieksekusi berurutan di sesi saat ini;
  • multisession - Mode paralel, perintah akan dieksekusi dalam sesi berjalan di latar belakang pada mesin saat ini, sementara sesi kerja Anda tidak akan diblokir;
  • cluster - Mode paralel, perintah akan dieksekusi pada mesin saat ini atau jarak jauh, mirip dengan bagaimana itu diterapkan dalam paket parallel .

Seluruh paket future didasarkan pada mengeksekusi perintah dalam proses latar belakang tanpa memblokir sesi saat ini. Jalankan eksekusi perintah mengikuti fungsi nama yang sama di future , jadi ketika kita menjalankan perintah:


 future({yadirGetKeyWords(Login = logins[4])}, packages = "ryandexdirect", globals = "logins") 

Sesi kami saat ini di R tidak diblokir, dan perintah dijalankan di latar belakang, menjalankan sesi R lainnya.


Anda dapat memeriksa status saat ini dari proses eksekusi dari ekspresi yang diberikan menggunakan fungsi yang resolved . Akhirnya, fungsi value digunakan untuk mendapatkan hasil eksekusi di future . Jika Anda menjalankan fungsi value lebih awal dari menjalankan future Anda dalam sesi berjalan paralel, maka sesi kerja saat ini akan diblokir sampai ekspresi sesi paralel selesai.


Contoh pekerjaan yang paling maju adalah penggunaan future hubungannya dengan promises - promises .


Kode 11: Contoh berbagi paket `masa depan` dan` janji`
 library(future) library(promises) #    plan(multiprocess) #      #    future.kw <- future({suppressMessages( yadirGetKeyWords(Login = logins[4]))}, packages = "ryandexdirect", globals = "logins") %...>% #     future, #      nrow() %...>% paste("words loaded") %...>% print() 

Paket promises memberikan satu set operator pipa yang sempurna melengkapi fungsionalitas future .


Pada contoh Kode 11 , di latar belakang, kami memulai proses mengunduh kata kunci dari satu akun iklan. Selanjutnya, operator pipa %...>% tanpa memblokir sesi kerja menunggu future , dan melakukan operasi yang tersisa. Sebagai hasil dari eksekusi kode, setelah menyelesaikan pekerjaan di future , jumlah kata kunci dari akun yang ditentukan akan ditampilkan di konsol:


 [1] "1855 words loaded" 

Di akhir artikel, contoh yang lebih ilustratif tentang sekelompok future dan promises akan ditunjukkan.

Secara default, paket yang future itu sendiri mengekspor seluruh ruang kerja ke setiap sesi berjalan paralel, tetapi Anda sendiri dapat menentukan daftar objek untuk diekspor menggunakan argumen global .


Untuk menghubungkan paket ke future harus melewati vektor yang berisi nama mereka ke argumen paket .


Sekarang kembali ke tugas kita, contoh kode berikut dalam mode paralel akan memuat daftar kata kunci dari 4 akun:


Kode 12: Contoh pemecahan masalah dengan menggunakan paket yang akan datang
 library(future) library(tictoc) #   plan("multisession", workers = 4) tic() #   futs <- lapply(logins, #      function(i) #        #   future({ yadirGetKeyWords(Login = i) %>% mutate(login = i) }, packages = c("ryandexdirect", "dplyr"))) completed <- sapply(futs, resolved) #    kw <- lapply(futs, value) #    toc() #    #     result.future <- dplyr::bind_rows(kw) 

Waktu Pimpin: 14.83 sec elapsed


Untuk mengunduh daftar kata kunci dalam mode multi-utas dari semua akun iklan yang tercantum dalam login vektor , Anda harus menjalankan future terpisah di latar belakang. Dalam Contoh Kode 12, kami menerapkan ini menggunakan fungsi lapply .


Hasil dari kerja lapply adalah daftar peluncuran di future . Anda dapat memeriksa status masing-masing menggunakan perintah sapply(futs, resolved) , yang akan mengembalikan vektor logis tempat TRUE akan berarti future terpenuhi, dan SALAH bahwa future sedang berlangsung.


Untuk mendapatkan hasil dari setiap future , setelah pekerjaan mereka selesai, kami menggunakan perintah lapply(futs, value) .


: result.future <- dplyr::bind_rows(kw) .


future


, (
future ), .


future.apply

future.apply future , .


13: future.apply
 library(future.apply) library(tictoc) #    plan("multisession", workers = 4) tic() #   kw.future.apply <- future_sapply(logins, #    ,   function(x) { #     yadirGetKeyWords(Login = x) %>% mutate(login = x) }, simplify = FALSE, #    #   future.packages = c("ryandexdirect", "dplyr"), future.globals = TRUE ) toc() #    

: 17.28 sec elapsed


13 , future.apply future , .


4 : plan("multisession", workers = 4) .


future_sapply logins . Yaitu , , sapply , .


future_sapply future.packages . future.globals . , .


furrr


future . purrr , furrr .


14: furrr
 library(furrr) library(tictoc) #   cl <- parallel::makeCluster(4) plan(cluster, workers = cl) tic() #   furrr.kw <- future_map(logins, ~ #   function(.x) yadirGetKeyWords(Login = .x) %>% mutate(login = .x), .options = future_options(packages = c("ryandexdirect", "dplyr"), globals = c())) toc() #    #      result.furrr <-dplyr::bind_rows(furrr.kw) 

: 15.45 sec elapsed


furrr purrr . purrr , .


.options . .options future_options , .


14 packages globals :


 .options = future_options(packages = c("ryandexdirect", "dplyr"), globals = c()) 


rbenchmark .


, future promises . .


, 20 4 () .


= (T[1] * 20) + (T[2] * 20) + (T[N] * 20)


15: future promises
 library(furrr) library(parallel) library(dplyr) library(future) library(ryandexdirect) library(tictoc) library(rbenchmark) #   logins <- c("login1", "login2", "login3", "login4") #        #        par par.furrr <- function(logins) { cl <- parallel::makeCluster(4) plan(cluster, workers = cl) furrr.kw <- future_map(logins, ~ yadirGetKeyWords(Login = .x) %>% mutate(login = .x), .options = future_options(packages = c("ryandexdirect", "dplyr"), globals = c())) result.furrr <-dplyr::bind_rows(furrr.kw) } par.future <- function(logins) { plan("multisession", workers = 4) futs <- lapply(logins, function(i) future({ yadirGetKeyWords(Login = i) %>% mutate(login = i) }, packages = c("ryandexdirect", "dplyr"))) completed <- sapply(futs, resolved) kw <- lapply(futs, value) result.future <- dplyr::bind_rows(kw) } par.future.apply <- function(logins) { plan("multisession", workers = 4) kw.future.apply <- future_sapply(logins, function(x) { yadirGetKeyWords(Login = x) %>% mutate(login = x) }, simplify = FALSE, future.packages = c("ryandexdirect", "dplyr"), future.globals = TRUE ) result.future.apply <- dplyr::bind_rows(kw.future.apply) } par.parallel <- function(logins) { cl <- parallel::makeCluster(4) clusterExport(cl = cl, varlist = "logins") clusterEvalQ(cl = cl, { library(ryandexdirect) library(dplyr) } ) parallel.kw <- parSapplyLB(cl = cl, X = logins, FUN = function(x) { yadirGetKeyWords(Login = x) %>% mutate(login = x) }, simplify = F) stopCluster(cl) result.parallel <- dplyr::bind_rows(parallel.kw) } #          seq seq.apply <- function(logins) { kw.sapply <- sapply( logins, function(x) { yadirGetKeyWords(Login = x) %>% mutate(login = x) }, simplify = FALSE ) result.sapply <- do.call("rbind", kw.sapply) } seq.purrr <- function(logins) { kw.purrr <- map_df( logins, ~ { yadirGetKeyWords(Login = .x) %>% mutate(login = .x) } ) result.purrr <- do.call("rbind", kw.purrr) } #       rbenchmark #   future + promises #  ,       #          plan(list(tweak(multisession, workers = 2), tweak(multisession, workers = 4))) tic() speed.test <- future({ #          within(benchmark(furrr = par.furrr(logins), future = par.future(logins), future.apply = par.future.apply(logins), parallel = par.parallel(logins), apply = seq.apply(logins), purrr = seq.purrr(logins), replications = c(20), columns = c('test', 'replications', 'elapsed'), order = c('elapsed', 'test')), { average = round(elapsed/replications, 2) }) }, packages = c("dplyr", "ryandexdirect", "rbenchmark", "parallel", "purrr", "future", "promises", "furrr", "future.apply"), globals = c("logins", "par.furrr", "par.future", "par.future.apply", "par.parallel", "seq.apply", "seq.purrr")) %...>% print() %...T>% toc() message("My Session is not blocked") 

3370 , .. .


. , future , promises , .


, . "My Session is not blocked", , , .. .


promises :


  • %...>%%>% , . Yaitu , resolved , future , value . , print .
  • %...T>%%T>% , , . , , , .. .. print , , .
  • %...T!% — .

15 plan tweak ( plan(list(tweak(multisession, workers = 2), tweak(multisession, workers = 4))) ), , 2 , future 4 .


:


 My Session is not blocked test replications elapsed average 4 parallel 20 393.02 19.65 1 furrr 20 402.09 20.10 2 future 20 431.19 21.56 3 future.apply 20 432.29 21.61 5 apply 20 847.77 42.39 6 purrr 20 864.19 43.21 3370.55 sec elapsed 

gambar


, parallel , . furrr , future future.apply .


1 , , . , API . .


, 4 , .


Kesimpulan


R, API.


, API . " R , 1" .


:


  • doSNOW / doParallel + foreach
  • future + promises
  • future.apply / furrr
  • parallel

, , .


, R .

Source: https://habr.com/ru/post/id448404/


All Articles