Tujuan dari paket masa depan adalah untuk menyediakan cara yang sangat sederhana dan seragam untuk mengevaluasi ekspresi R secara tidak sinkron menggunakan berbagai sumber daya yang tersedia untuk pengguna.
Dalam pemrograman, masa depan adalah abstraksi untuk nilai yang mungkin tersedia di beberapa titik di masa depan. Keadaan masa depan dapat tidak terselesaikan atau diselesaikan . Segera setelah diselesaikan, nilainya tersedia secara instan. Jika nilainya dipertanyakan saat masa depan masih belum terselesaikan, proses saat ini diblokir sampai masa depan diselesaikan. Dimungkinkan untuk memeriksa apakah masa depan diselesaikan atau tidak tanpa memblokir. Tepatnya dan kapan masa depan diselesaikan tergantung pada strategi apa yang digunakan untuk mengevaluasinya. Misalnya, masa depan dapat diselesaikan dengan menggunakan strategi berurutan, yang berarti diselesaikan dalam sesi R saat ini. Strategi lain mungkin untuk menyelesaikan futures secara tidak sinkron, misalnya, dengan mengevaluasi ekspresi secara paralel pada mesin saat ini atau bersamaan pada kluster komputasi.
Berikut adalah contoh yang menggambarkan bagaimana dasar -dasar masa depan bekerja. Pertama, pertimbangkan cuplikan kode berikut yang menggunakan kode R biasa:
> v <- {
+ cat( " Hello world! n " )
+ 3.14
+ }
Hello world !
> v
[ 1 ] 3.14
Ini bekerja dengan menetapkan nilai ekspresi ke variabel v
dan kami kemudian mencetak nilai v
. Selain itu, ketika ekspresi untuk v
dievaluasi kami juga mencetak pesan.
Berikut ini adalah cuplikan kode yang sama yang dimodifikasi untuk menggunakan futures sebagai gantinya:
> library( future )
> v % <- % {
+ cat( " Hello world! n " )
+ 3.14
+ }
> v
Hello world !
[ 1 ] 3.14
Perbedaannya adalah bagaimana v
dibangun; Dengan polos R kami menggunakan <-
sedangkan dengan masa depan kami menggunakan %<-%
. Perbedaan lainnya adalah bahwa output disampaikan setelah masa depan diselesaikan (bukan selama) dan ketika nilainya ditanya (lihat vignette 'outputting text').
Jadi mengapa masa depan bermanfaat? Karena kita dapat memilih untuk mengevaluasi ekspresi di masa depan dalam proses R yang terpisah secara tidak sinkron dengan hanya beralih pengaturan sebagai:
> library( future )
> plan( multisession )
> v % <- % {
+ cat( " Hello world! n " )
+ 3.14
+ }
> v
Hello world !
[ 1 ] 3.14
Dengan futures asinkron, proses saat ini/utama tidak memblokir, yang berarti tersedia untuk pemrosesan lebih lanjut saat masa depan diselesaikan dalam proses terpisah yang berjalan di latar belakang. Dengan kata lain, masa depan memberikan konstruksi yang sederhana namun kuat untuk pemrosesan paralel dan / atau terdistribusi dalam R.
Sekarang, jika Anda tidak dapat repot-repot membaca semua detail seluk-beluk tentang masa depan, tetapi hanya ingin mencobanya, maka lewati sampai akhir untuk bermain dengan demo Mandelbrot menggunakan evaluasi paralel dan non-paralel.
Futures dapat dibuat baik secara implisit atau eksplisit . Dalam contoh pengantar di atas kami menggunakan futures implisit yang dibuat melalui konstruk v %<-% { expr }
. Alternatif adalah futures eksplisit menggunakan konstruksi f <- future({ expr })
dan v <- value(f)
. Dengan ini, contoh kami dapat ditulis sebagai:
> library( future )
> f <- future({
+ cat( " Hello world! n " )
+ 3.14
+ })
> v <- value( f )
Hello world !
> v
[ 1 ] 3.14
Baik gaya konstruk di masa depan berfungsi sama (*) dengan baik. Gaya implisit paling mirip dengan bagaimana kode R biasa ditulis. Pada prinsipnya, yang harus Anda lakukan adalah mengganti <-
dengan %<-%
untuk mengubah penugasan menjadi penugasan di masa depan. Di sisi lain, kesederhanaan ini juga bisa menipu, terutama ketika masa depan asinkron digunakan. Sebaliknya, gaya eksplisit membuatnya jauh lebih jelas bahwa masa depan digunakan, yang menurunkan risiko kesalahan dan lebih baik mengkomunikasikan desain kepada orang lain yang membaca kode Anda.
(*) Ada kasus di mana %<-%
tidak dapat digunakan tanpa beberapa modifikasi (kecil). Kami akan kembali ke ini di bagian 'kendala saat menggunakan futures implisit' di dekat akhir dokumen ini.
Untuk meringkas, untuk masa depan yang eksplisit, kami menggunakan:
f <- future({ expr })
- menciptakan masa depanv <- value(f)
- Mendapat nilai masa depan (blok jika belum terselesaikan)Untuk masa depan implisit, kami menggunakan:
v %<-% { expr }
- Menciptakan masa depan dan janji nilainyaAgar tetap sederhana, kami akan menggunakan gaya implisit dalam sisa dokumen ini, tetapi semuanya yang dibahas juga akan berlaku untuk masa depan yang eksplisit.
Paket di masa depan mengimplementasikan jenis masa depan berikut:
Nama | Oses | Keterangan |
---|---|---|
sinkronis: | Non-paralel: | |
sequential | semua | secara berurutan dan dalam proses R saat ini |
Asynchronous: | Paralel : | |
multisession | semua | Sesi latar belakang (pada mesin saat ini) |
multicore | Bukan Windows/bukan rstudio | Proses Forked R (pada mesin saat ini) |
cluster | semua | Sesi R eksternal pada mesin saat ini, lokal, dan/atau jarak jauh |
Paket di masa depan dirancang sedemikian rupa sehingga dukungan untuk strategi tambahan juga dapat diimplementasikan. Misalnya, paket Future.callr menyediakan backend di masa depan yang mengevaluasi masa depan dalam proses latar belakang R yang menggunakan paket callr - mereka bekerja mirip dengan berjangka multisession
tetapi memiliki beberapa keuntungan. Lanjutan, Paket Future.BatchTools menyediakan futures untuk semua jenis fungsi cluster ("backends") yang didukung paket BatchTools. Secara khusus, berjangka untuk mengevaluasi ekspresi R melalui penjadwal pekerjaan seperti Slurm, Torque/PBS, Oracle/Sun Grid Engine (SGE) dan Load Sharing Facility (LSF) juga tersedia.
Secara default, ekspresi di masa depan dievaluasi dengan penuh semangat (= secara instan) dan secara sinkron (dalam sesi R saat ini). Strategi evaluasi ini disebut sebagai "berurutan". Di bagian ini, kita akan melalui masing -masing strategi ini dan mendiskusikan kesamaan yang mereka miliki dan bagaimana mereka berbeda.
Sebelum melalui masing -masing strategi masa depan yang berbeda, mungkin bermanfaat untuk mengklarifikasi tujuan API masa depan (sebagaimana didefinisikan oleh paket masa depan). Saat pemrograman dengan futures, seharusnya tidak masalah apa pun strategi masa depan yang digunakan untuk mengeksekusi kode. Ini karena kita tidak dapat benar -benar tahu sumber daya komputasi apa yang dimiliki pengguna sehingga pilihan strategi evaluasi harus ada di tangan pengguna dan bukan pengembang. Dengan kata lain, kode tidak boleh membuat asumsi pada jenis masa depan yang digunakan, misalnya sinkron atau asinkron.
Salah satu desain API di masa depan adalah merangkum perbedaan apa pun sehingga semua jenis masa depan akan tampak bekerja sama. Ini meskipun ekspresi dapat dievaluasi secara lokal di sesi R saat ini atau di seluruh dunia dalam sesi R jarak jauh. Keuntungan lain yang jelas dari memiliki API dan perilaku yang konsisten di antara berbagai jenis masa depan adalah membantu saat membuat prototipe. Biasanya orang akan menggunakan evaluasi berurutan sambil membangun skrip dan, kemudian, ketika skrip dikembangkan sepenuhnya, seseorang dapat mengaktifkan pemrosesan asinkron.
Karena itu, default dari strategi yang berbeda adalah sedemikian rupa sehingga hasil dan efek samping dari mengevaluasi ekspresi di masa depan serupa mungkin. Lebih khusus lagi, berikut ini berlaku untuk semua masa depan:
Semua evaluasi dilakukan di lingkungan lokal (yaitu local({ expr })
) sehingga penugasan tidak mempengaruhi lingkungan panggilan. Ini wajar ketika mengevaluasi dalam proses R eksternal, tetapi juga ditegakkan saat mengevaluasi dalam sesi R saat ini.
Ketika masa depan dibangun, variabel global diidentifikasi . Untuk evaluasi asinkron, global diekspor ke proses R/sesi yang akan mengevaluasi ekspresi masa depan. Untuk masa depan berurutan dengan evaluasi malas ( lazy = TRUE
), global "beku" (dikloning ke lingkungan lokal di masa depan). Juga, untuk melindungi dari mengekspor objek yang terlalu besar secara tidak sengaja, ada pernyataan bawaan bahwa ukuran total semua global kurang dari ambang batas yang diberikan (dapat dikendalikan melalui opsi, lih. help("future.options")
). Jika ambang batas terlampaui, kesalahan informatif dilemparkan.
Ekspresi di masa depan hanya dievaluasi sekali . Segera setelah nilai (atau kesalahan) telah dikumpulkan, itu akan tersedia untuk semua permintaan berikutnya.
Berikut adalah contoh yang menggambarkan bahwa semua penugasan dilakukan untuk lingkungan lokal:
> plan( sequential )
> a <- 1
> x % <- % {
+ a <- 2
+ 2 * a
+ }
> x
[ 1 ] 4
> a
[ 1 ] 1
Sekarang kami siap untuk mengeksplorasi berbagai strategi masa depan.
Futures sinkron diselesaikan satu demi satu dan paling umum oleh proses R yang membuatnya. Ketika masa depan yang sinkron diselesaikan, itu menghalangi proses utama sampai diselesaikan.
Berurutan berjangka adalah default kecuali ditentukan lain. Mereka dirancang untuk berperilaku serupa dengan evaluasi R reguler sambil tetap memenuhi API di masa depan dan perilakunya. Berikut adalah contoh yang menggambarkan propertinya:
> plan( sequential )
> pid <- Sys.getpid()
> pid
[ 1 ] 1437557
> a % <- % {
+ pid <- Sys.getpid()
+ cat( " Future 'a' ... n " )
+ 3.14
+ }
> b % <- % {
+ rm( pid )
+ cat( " Future 'b' ... n " )
+ Sys.getpid()
+ }
> c % <- % {
+ cat( " Future 'c' ... n " )
+ 2 * a
+ }
Future ' a ' ...
> b
Future ' b ' ...
[ 1 ] 1437557
> c
Future ' c ' ...
[ 1 ] 6.28
> a
[ 1 ] 3.14
> pid
[ 1 ] 1437557
Karena evaluasi berurutan yang bersemangat sedang terjadi, masing -masing dari tiga masa depan diselesaikan secara instan pada saat itu dibuat. Perhatikan juga bagaimana pid
di lingkungan panggilan, yang diberi ID proses dari proses saat ini, tidak ditimpa atau dihapus. Ini karena masa depan dievaluasi di lingkungan lokal. Karena pemrosesan sinkron (uni) digunakan, masa depan b
diselesaikan dengan proses utama (masih dalam lingkungan lokal), itulah sebabnya nilai b
dan pid
adalah sama.
Selanjutnya, kita akan beralih ke futures asinkron, yang merupakan masa depan yang diselesaikan di latar belakang. Dengan desain, masa depan ini tidak memblokir, yaitu, setelah dibuat proses panggilan tersedia untuk tugas-tugas lain termasuk menciptakan masa depan tambahan. Hanya ketika proses panggilan mencoba untuk mengakses nilai masa depan yang belum diselesaikan, atau mencoba menciptakan masa depan asinkron lain ketika semua proses R yang tersedia sibuk melayani masa depan lainnya, yang diblokir.
Kami mulai dengan berjangka multisesi karena didukung oleh semua sistem operasi. Masa depan multisesi dievaluasi dalam sesi R latar belakang yang berjalan pada mesin yang sama dengan proses panggilan R. Berikut adalah contoh kami dengan evaluasi multisesi:
> plan( multisession )
> pid <- Sys.getpid()
> pid
[ 1 ] 1437557
> a % <- % {
+ pid <- Sys.getpid()
+ cat( " Future 'a' ... n " )
+ 3.14
+ }
> b % <- % {
+ rm( pid )
+ cat( " Future 'b' ... n " )
+ Sys.getpid()
+ }
> c % <- % {
+ cat( " Future 'c' ... n " )
+ 2 * a
+ }
Future ' a ' ...
> b
Future ' b ' ...
[ 1 ] 1437616
> c
Future ' c ' ...
[ 1 ] 6.28
> a
[ 1 ] 3.14
> pid
[ 1 ] 1437557
Hal pertama yang kami amati adalah bahwa nilai -nilai a
, c
dan pid
sama seperti sebelumnya. Namun, kami melihat bahwa b
berbeda dari sebelumnya. Ini karena masa depan b
dievaluasi dalam proses R yang berbeda dan oleh karena itu ia mengembalikan ID proses yang berbeda.
Ketika evaluasi multisesi digunakan, paket meluncurkan satu set sesi R di latar belakang yang akan melayani berjangka multisesi dengan mengevaluasi ekspresi mereka saat dibuat. Jika semua sesi latar belakang sibuk melayani masa depan lainnya, penciptaan masa depan multisesi berikutnya diblokir sampai sesi latar belakang tersedia lagi. Jumlah total proses latar belakang yang diluncurkan diputuskan oleh nilai availableCores()
, misalnya
> availableCores()
mc.cores
2
Hasil khusus ini memberi tahu kami bahwa opsi mc.cores
telah ditetapkan sedemikian rupa sehingga kami diizinkan untuk menggunakan total dua (2) proses termasuk proses utama. Dengan kata lain, dengan pengaturan ini, akan ada dua (2) proses latar belakang yang melayani masa depan multisesi. availableCores()
juga gesit untuk berbagai opsi dan variabel lingkungan sistem. Misalnya, jika penjadwal kluster komputasi digunakan (misalnya torsi/PBS dan Slurm), mereka menetapkan variabel lingkungan spesifik yang menentukan jumlah core yang dialokasikan untuk pekerjaan tertentu; availableCores()
mengakui ini juga. Jika tidak ada lagi yang ditentukan, semua core yang tersedia pada mesin akan digunakan, lih. parallel::detectCores()
. Untuk detail lebih lanjut, silakan lihat help("availableCores", package = "parallelly")
.
Pada sistem operasi di mana R mendukung forking proses, yang pada dasarnya adalah semua sistem operasi kecuali Windows, alternatif untuk pemijahan sesi R di latar belakang adalah untuk membayar proses R yang ada. Untuk menggunakan Multicore Futures, saat didukung, tentukan:
plan( multicore )
Sama seperti untuk berjangka multisesi, jumlah maksimum proses paralel yang berjalan akan diputuskan oleh availableCores()
, karena dalam kedua kasus evaluasi dilakukan pada mesin lokal.
Forking proses R bisa lebih cepat daripada bekerja dengan sesi R terpisah yang berjalan di latar belakang. Salah satu alasannya adalah bahwa overhead mengekspor global besar ke sesi latar belakang bisa lebih besar daripada saat bercabang, dan karenanya memori bersama, digunakan. Di sisi lain, memori bersama hanya dibaca , yang berarti modifikasi apa pun pada objek bersama oleh salah satu proses bercabang ("pekerja") akan menyebabkan salinan oleh sistem operasi. Ini juga dapat terjadi ketika kolektor sampah berjalan dalam salah satu proses bercabang.
Di sisi lain, forking proses juga dianggap tidak stabil di beberapa lingkungan R. Misalnya, saat menjalankan R dari dalam forking proses rStudio dapat mengakibatkan sesi R yang hancur. Karena itu, paket masa depan menonaktifkan Multicore Futures secara default saat berjalan dari RStudio. Lihat help("supportsMulticore")
untuk lebih jelasnya.
Cluster Futures mengevaluasi ekspresi pada cluster ad-hoc (seperti yang diimplementasikan oleh paket paralel). Misalnya, asumsikan Anda memiliki akses ke tiga node n1
, n2
dan n3
, Anda kemudian dapat menggunakannya untuk evaluasi asinkron sebagai:
> plan( cluster , workers = c( " n1 " , " n2 " , " n3 " ))
> pid <- Sys.getpid()
> pid
[ 1 ] 1437557
> a % <- % {
+ pid <- Sys.getpid()
+ cat( " Future 'a' ... n " )
+ 3.14
+ }
> b % <- % {
+ rm( pid )
+ cat( " Future 'b' ... n " )
+ Sys.getpid()
+ }
> c % <- % {
+ cat( " Future 'c' ... n " )
+ 2 * a
+ }
Future ' a ' ...
> b
Future ' b ' ...
[ 1 ] 1437715
> c
Future ' c ' ...
[ 1 ] 6.28
> a
[ 1 ] 3.14
> pid
[ 1 ] 1437557
Segala jenis cluster yang parallel::makeCluster()
dapat digunakan untuk cluster futures. Misalnya, cluster di atas dapat diatur secara eksplisit sebagai:
cl <- parallel :: makeCluster(c( " n1 " , " n2 " , " n3 " ))
plan( cluster , workers = cl )
Juga, dianggap gaya yang baik untuk menutup cluster cl
ketika tidak lagi diperlukan, yaitu memanggil parallel::stopCluster(cl)
. Namun, itu akan ditutup sendiri jika proses utama diakhiri. Untuk informasi lebih lanjut tentang cara mengatur dan mengelola cluster tersebut, lihat help("makeCluster", package = "parallel")
. Cluster yang dibuat secara implisit menggunakan plan(cluster, workers = hosts)
di mana hosts
adalah vektor karakter juga akan ditutup ketika sesi R utama berakhir, atau ketika strategi masa depan diubah, misalnya dengan panggilan panggilan plan(sequential)
.
Perhatikan bahwa dengan pengaturan otentikasi otomatis (misalnya pasangan kunci SSH), tidak ada yang mencegah kita menggunakan pendekatan yang sama untuk menggunakan sekelompok mesin jarak jauh.
Jika Anda ingin menjalankan beberapa pekerja di setiap node, replikasi nama node sebanyak jumlah pekerja yang akan dijalankan pada node itu. Misalnya,
> plan(cluster, workers = c(rep("n1", times = 3), "n2", rep("n3", times = 5)))
akan menjalankan tiga pekerja di n1
, satu pada n2
, dan lima pada n3
, total sembilan pekerja paralel.
Sejauh ini kami telah membahas apa yang dapat disebut sebagai "topologi datar" masa depan, yaitu, semua masa depan dibuat dan ditugaskan ke lingkungan yang sama. Namun, tidak ada yang menghentikan kita dari menggunakan "topologi bersarang" masa depan, di mana satu set masa depan dapat, pada gilirannya, menciptakan set masa depan lain secara internal dan sebagainya.
Misalnya, berikut adalah contoh dari dua "atas" berjangka ( a
dan b
) yang menggunakan evaluasi multisesi dan di mana masa depan kedua ( b
) pada gilirannya menggunakan dua futures internal:
> plan( multisession )
> pid <- Sys.getpid()
> a % <- % {
+ cat( " Future 'a' ... n " )
+ Sys.getpid()
+ }
> b % <- % {
+ cat( " Future 'b' ... n " )
+ b1 % <- % {
+ cat( " Future 'b1' ... n " )
+ Sys.getpid()
+ }
+ b2 % <- % {
+ cat( " Future 'b2' ... n " )
+ Sys.getpid()
+ }
+ c( b.pid = Sys.getpid(), b1.pid = b1 , b2.pid = b2 )
+ }
> pid
[ 1 ] 1437557
> a
Future ' a ' ...
[ 1 ] 1437804
> b
Future ' b ' ...
Future ' b1 ' ...
Future ' b2 ' ...
b.pid b1.pid b2.pid
1437805 1437805 1437805
Dengan memeriksa ID proses, kami melihat bahwa ada total tiga proses berbeda yang terlibat untuk menyelesaikan masa depan. Ada proses R utama (PID 1437557), dan ada dua proses yang digunakan oleh a
(PID 1437804) dan b
(PID 1437805). Namun, dua masa depan ( b1
dan b2
) yang bersarang oleh b
dievaluasi dengan proses R yang sama dengan b
. Ini karena berjangka bersarang menggunakan evaluasi berurutan kecuali ditentukan lain. Ada beberapa alasan untuk ini, tetapi alasan utamanya adalah bahwa ia melindungi kita dari memunculkan sejumlah besar proses latar belakang secara tidak sengaja, misalnya melalui panggilan rekursif.
Untuk menentukan jenis topologi evaluasi yang berbeda, selain tingkat futures pertama yang diselesaikan dengan evaluasi multisesi dan tingkat kedua dengan evaluasi sekuensial, kami dapat memberikan daftar strategi evaluasi untuk plan()
. Pertama, strategi evaluasi yang sama seperti di atas dapat secara eksplisit ditentukan sebagai:
plan( list ( multisession , sequential ))
Kami benar -benar akan mendapatkan perilaku yang sama jika kami mencoba dengan berbagai tingkat evaluasi multisesi;
> plan( list ( multisession , multisession ))
[ ... ]
> pid
[ 1 ] 1437557
> a
Future ' a ' ...
[ 1 ] 1437901
> b
Future ' b ' ...
Future ' b1 ' ...
Future ' b2 ' ...
b.pid b1.pid b2.pid
1437902 1437902 1437902
Alasan untuk ini adalah, juga di sini, untuk melindungi kami dari meluncurkan lebih banyak proses daripada apa yang dapat didukung mesin. Secara internal, ini dilakukan dengan mengatur mc.cores = 1
sedemikian rupa sehingga fungsi seperti parallel::mclapply()
akan kembali untuk berjalan secara berurutan. Ini adalah kasus untuk evaluasi multisesi dan multicore.
Melanjutkan, jika kita memulai dengan evaluasi berurutan dan kemudian menggunakan evaluasi multisesi untuk setiap berjangka bersarang, kita mendapatkan:
> plan( list ( sequential , multisession ))
[ ... ]
> pid
[ 1 ] 1437557
> a
Future ' a ' ...
[ 1 ] 1437557
> b
Future ' b ' ...
Future ' b1 ' ...
Future ' b2 ' ...
b.pid b1.pid b2.pid
1437557 1438017 1438016
yang dengan jelas menunjukkan bahwa a
dan b
diselesaikan dalam proses panggilan (PID 1437557) sedangkan dua berjangka bersarang ( b1
dan b2
) diselesaikan dalam dua proses R terpisah (PID 1438017 dan 1438016).
Setelah mengatakan ini, memang mungkin untuk menggunakan strategi evaluasi multisesi bersarang, jika kami secara eksplisit menentukan (membaca kekuatan ) jumlah inti yang tersedia di setiap tingkat. Untuk melakukan ini, kita perlu "mengubah" pengaturan default, yang dapat dilakukan sebagai berikut:
> plan( list (tweak( multisession , workers = 2 ), tweak( multisession ,
+ workers = 2 )))
[ ... ]
> pid
[ 1 ] 1437557
> a
Future ' a ' ...
[ 1 ] 1438105
> b
Future ' b ' ...
Future ' b1 ' ...
Future ' b2 ' ...
b.pid b1.pid b2.pid
1438106 1438211 1438212
Pertama, kita melihat bahwa baik a
dan b
diselesaikan dalam proses yang berbeda (PIDS 1438105 dan 1438106) daripada proses panggilan (PID 1437557). Kedua, dua berjangka bersarang ( b1
dan b2
) diselesaikan dalam dua proses R lainnya (PID 1438211 dan 1438212).
Untuk detail lebih lanjut tentang bekerja dengan berjangka bersarang dan strategi evaluasi yang berbeda di setiap tingkat, lihat Vignette 'Futures in R: Future Topologies'.
Dimungkinkan untuk memeriksa apakah masa depan telah diselesaikan atau tidak tanpa memblokir. Ini dapat dilakukan dengan menggunakan fungsi resolved(f)
, yang mengambil masa depan eksplisit f
sebagai input. Jika kita bekerja dengan berjangka implisit (seperti dalam semua contoh di atas), kita dapat menggunakan fungsi f <- futureOf(a)
untuk mengambil masa depan eksplisit dari yang tersirat. Misalnya,
> plan( multisession )
> a % <- % {
+ cat( " Future 'a' ... " )
+ Sys.sleep( 2 )
+ cat( " done n " )
+ Sys.getpid()
+ }
> cat( " Waiting for 'a' to be resolved ... n " )
Waiting for ' a ' to be resolved ...
> f <- futureOf( a )
> count <- 1
> while ( ! resolved( f )) {
+ cat( count , " n " )
+ Sys.sleep( 0.2 )
+ count <- count + 1
+ }
1
2
3
4
5
6
7
8
9
10
> cat( " Waiting for 'a' to be resolved ... DONE n " )
Waiting for ' a ' to be resolved ... DONE
> a
Future ' a ' ... done
[ 1 ] 1438287
Terkadang masa depan bukan yang Anda harapkan. Jika terjadi kesalahan saat mengevaluasi masa depan, kesalahan disebarkan dan dilemparkan sebagai kesalahan dalam lingkungan panggilan ketika nilai masa depan diminta . Misalnya, jika kita menggunakan evaluasi malas pada masa depan yang menghasilkan kesalahan, kita mungkin melihat sesuatu seperti
> plan( sequential )
> b <- " hello "
> a % <- % {
+ cat( " Future 'a' ... n " )
+ log( b )
+ } % lazy % TRUE
> cat( " Everything is still ok although we have created a future that will fail. n " )
Everything is still ok although we have created a future that will fail.
> a
Future ' a ' ...
Error in log( b ) : non - numeric argument to mathematical function
Kesalahan dilemparkan setiap kali nilainya diminta, yaitu, jika kami mencoba mendapatkan nilainya lagi akan menghasilkan kesalahan yang sama (dan output):
> a
Future ' a ' ...
Error in log( b ) : non - numeric argument to mathematical function
In addition : Warning message :
restarting interrupted promise evaluation
Untuk melihat panggilan terakhir di tumpukan panggilan yang memberikan kesalahan, kita dapat menggunakan fungsi backtrace()
(*) di masa depan, yaitu
> backtrace( a )
[[ 1 ]]
log( a )
(*) traceback()
tidak memberikan informasi yang relevan dalam konteks masa depan. Selain itu, sayangnya tidak mungkin untuk melihat daftar panggilan (evaluasi ekspresi) yang mengarah ke kesalahan; Hanya panggilan yang memberikan kesalahan (ini karena keterbatasan dalam tryCatch()
yang digunakan secara internal).
Setiap kali ekspresi R harus dievaluasi secara tidak sinkron (secara paralel) atau secara berurutan melalui evaluasi malas, objek global (alias "bebas") harus diidentifikasi dan diteruskan ke evaluator. Mereka perlu dilewati persis seperti pada saat itu di masa depan diciptakan, karena, untuk evaluasi malas, global mungkin berubah antara ketika dibuat dan ketika diselesaikan. Untuk pemrosesan asinkron, alasan global perlu diidentifikasi adalah sehingga mereka dapat diekspor ke proses yang mengevaluasi masa depan.
Paket di masa depan mencoba mengotomatiskan tugas -tugas ini sejauh mungkin. Ini melakukan ini dengan bantuan paket Globals, yang menggunakan inspeksi kode statis untuk mengidentifikasi variabel global. Jika variabel global diidentifikasi, itu ditangkap dan disediakan untuk proses evaluasi. Selain itu, jika global didefinisikan dalam paket, maka global itu tidak diekspor. Sebaliknya, itu memastikan bahwa paket yang sesuai terlampir ketika masa depan dievaluasi. Ini tidak hanya lebih baik mencerminkan pengaturan sesi R utama, tetapi juga meminimalkan kebutuhan untuk mengekspor global, yang menghemat tidak hanya memori tetapi juga waktu dan bandwidth, terutama saat menggunakan node komputasi jarak jauh.
Akhirnya, harus diklarifikasi bahwa mengidentifikasi global dari inspeksi kode statis saja adalah masalah yang menantang. Akan selalu ada kasus sudut di mana identifikasi otomatis global gagal sehingga baik global palsu diidentifikasi (lebih sedikit perhatian) atau beberapa global sejati hilang (yang akan mengakibatkan kesalahan run-time atau mungkin hasil yang salah). Vignette 'Futures in R: Masalah umum dengan solusi' memberikan contoh kasus umum dan menjelaskan bagaimana menghindarinya serta bagaimana membantu paket untuk mengidentifikasi global atau mengabaikan global yang diidentifikasi secara salah. Jika itu tidak cukup, selalu mungkin untuk secara manual menentukan variabel global dengan nama mereka (misalnya globals = c("a", "slow_sum")
) atau sebagai pasangan nilai nama (misalnya globals = list(a = 42, slow_sum = my_sum)
).
Ada satu batasan dengan masa depan implisit yang tidak ada untuk yang eksplisit. Karena masa depan yang eksplisit sama seperti objek lain di R itu dapat ditetapkan di mana saja/untuk apa pun. Misalnya, kita dapat membuat beberapa dari mereka dalam satu lingkaran dan menugaskannya ke daftar, misalnya
> plan( multisession )
> f <- list ()
> for ( ii in 1 : 3 ) {
+ f [[ ii ]] <- future({
+ Sys.getpid()
+ })
+ }
> v <- lapply( f , FUN = value )
> str( v )
List of 3
$ : int 1438377
$ : int 1438378
$ : int 1438377
Ini tidak mungkin dilakukan saat menggunakan masa depan implisit. Ini karena operator penugasan %<-%
tidak dapat digunakan dalam semua kasus di mana operator penugasan <-
dapat digunakan. Ini hanya dapat digunakan untuk menetapkan nilai di masa depan ke lingkungan (termasuk lingkungan panggilan) seperti bagaimana assign(name, value, envir)
berfungsi. Namun, kami dapat menetapkan masa depan implisit ke lingkungan menggunakan indeks bernama , misalnya
> plan( multisession )
> v <- new.env()
> for ( name in c( " a " , " b " , " c " )) {
+ v [[ name ]] % <- % {
+ Sys.getpid()
+ }
+ }
> v <- as.list( v )
> str( v )
List of 3
$ a : int 1438485
$ b : int 1438486
$ c : int 1438485
Di sini as.list(v)
sampai semua masa depan di lingkungan v
telah diselesaikan. Kemudian nilai -nilai mereka dikumpulkan dan dikembalikan sebagai daftar reguler.
Jika indeks numerik diperlukan, maka daftar lingkungan dapat digunakan. Daftar Lingkungan, yang diimplementasikan oleh Paket ListenV, adalah lingkungan reguler dengan operator subsetting yang disesuaikan sehingga memungkinkan untuk mengindeksnya seperti bagaimana daftar dapat diindeks. Dengan menggunakan lingkungan daftar di mana kami akan menggunakan daftar, kami juga dapat menetapkan futures implisit ke objek seperti daftar menggunakan indeks numerik. Misalnya,
> library( listenv )
> plan( multisession )
> v <- listenv()
> for ( ii in 1 : 3 ) {
+ v [[ ii ]] % <- % {
+ Sys.getpid()
+ }
+ }
> v <- as.list( v )
> str( v )
List of 3
$ : int 1438582
$ : int 1438583
$ : int 1438582
Seperti sebelumnya, blok as.list(v)
sampai semua masa depan diselesaikan.
Untuk melihat ilustrasi langsung bagaimana berbagai jenis masa depan dievaluasi, jalankan demo mandelbrot dari paket ini. Pertama, coba dengan evaluasi berurutan,
library( future )
plan( sequential )
demo( " mandelbrot " , package = " future " , ask = FALSE )
yang menyerupai bagaimana skrip akan berjalan jika masa depan tidak digunakan. Kemudian, coba evaluasi multisesi, yang menghitung bidang mandelbrot yang berbeda menggunakan proses paralel yang berjalan di latar belakang. Mencoba,
plan( multisession )
demo( " mandelbrot " , package = " future " , ask = FALSE )
Akhirnya, jika Anda memiliki akses ke beberapa mesin, Anda dapat mencoba mengatur sekelompok pekerja dan menggunakannya, misalnya
plan( cluster , workers = c( " n2 " , " n5 " , " n6 " , " n6 " , " n9 " ))
demo( " mandelbrot " , package = " future " , ask = FALSE )
Paket R Masa Depan tersedia di cran dan dapat diinstal dalam R sebagai:
install.packages( " future " )
Untuk menginstal versi pra-rilis yang tersedia di Git Branch develop
di GitHub, gunakan:
remotes :: install_github( " futureverse/future " , ref = " develop " )
Ini akan menginstal paket dari sumber.
Untuk berkontribusi pada paket ini, silakan lihat Contributing.md.