วัตถุประสงค์ของแพ็คเกจในอนาคตคือการจัดหาวิธีการประเมิน R Expressions แบบอะซิงโครนัสโดยใช้ทรัพยากรต่าง ๆ ที่มีให้กับผู้ใช้
ในการเขียนโปรแกรม อนาคต เป็นสิ่งที่เป็นนามธรรมสำหรับ ค่า ที่อาจมีอยู่ในบางจุดในอนาคต สถานะของอนาคตสามารถ ไม่ได้รับการแก้ไข หรือ แก้ไข ทันทีที่ได้รับการแก้ไขค่าจะพร้อมใช้งานทันที หากค่าถูกสอบถามในขณะที่อนาคตยังไม่ได้รับการแก้ไขกระบวนการปัจจุบันจะ ถูกบล็อก จนกว่าอนาคตจะได้รับการแก้ไข เป็นไปได้ที่จะตรวจสอบว่าอนาคตได้รับการแก้ไขหรือไม่โดยไม่ต้องปิดกั้น วิธีการแก้ไขฟิวเจอร์สได้รับการแก้ไขอย่างไรและเมื่อใดขึ้นอยู่กับกลยุทธ์ที่ใช้ในการประเมิน ตัวอย่างเช่นอนาคตสามารถแก้ไขได้โดยใช้กลยุทธ์ลำดับซึ่งหมายความว่าได้รับการแก้ไขในเซสชัน R ปัจจุบัน กลยุทธ์อื่น ๆ อาจเป็นการแก้ไขฟิวเจอร์สแบบอะซิงโครนัสตัวอย่างเช่นโดยการประเมินการแสดงออกในแบบคู่ขนานบนเครื่องปัจจุบันหรือพร้อมกันบนคลัสเตอร์การคำนวณ
นี่คือตัวอย่างที่แสดงให้เห็นว่าพื้นฐานของอนาคตทำงานอย่างไร ก่อนอื่นให้พิจารณาตัวอย่างโค้ดต่อไปนี้ที่ใช้รหัส R ธรรมดา:
> v <- {
+ cat( " Hello world! n " )
+ 3.14
+ }
Hello world !
> v
[ 1 ] 3.14
มันทำงานโดยการกำหนดค่าของนิพจน์ให้กับตัวแปร v
และจากนั้นเราก็พิมพ์ค่าของ v
ยิ่งไปกว่านั้นเมื่อมีการประเมินนิพจน์สำหรับ v
เรายังพิมพ์ข้อความ
นี่คือตัวอย่างรหัสเดียวกันที่แก้ไขเพื่อใช้ฟิวเจอร์สแทน:
> library( future )
> v % <- % {
+ cat( " Hello world! n " )
+ 3.14
+ }
> v
Hello world !
[ 1 ] 3.14
ความแตกต่างคือวิธีการสร้าง v
; ด้วยความธรรมดาเราใช้ <-
ในขณะที่มีอนาคตเราใช้ %<-%
ความแตกต่างอื่น ๆ คือเอาต์พุตจะถูกถ่ายทอด หลังจาก อนาคตได้รับการแก้ไข (ไม่ใช่ในระหว่าง) และเมื่อค่าถูกสอบถาม (ดูบทความสั้น ๆ 'เอาท์พุทข้อความ')
เหตุใดอนาคตจึงมีประโยชน์? เพราะเราสามารถเลือกที่จะประเมินการแสดงออกในอนาคตในกระบวนการ R แยกอะซิงโครนัสโดยเพียงแค่เปลี่ยนการตั้งค่าเป็น:
> library( future )
> plan( multisession )
> v % <- % {
+ cat( " Hello world! n " )
+ 3.14
+ }
> v
Hello world !
[ 1 ] 3.14
ด้วยฟิวเจอร์สแบบอะซิงโครนัสกระบวนการ R ปัจจุบัน/หลักจะ ไม่ บล็อกซึ่งหมายความว่ามีให้สำหรับการประมวลผลเพิ่มเติมในขณะที่ฟิวเจอร์สได้รับการแก้ไขในกระบวนการแยกต่างหากที่ทำงานในพื้นหลัง กล่าวอีกนัยหนึ่งฟิวเจอร์สเป็นโครงสร้างที่เรียบง่าย แต่ยังทรงพลังสำหรับการประมวลผลแบบขนานและ / หรือแบบกระจายในอาร์
ตอนนี้ถ้าคุณไม่สามารถอ่านรายละเอียดทั้งหมดเกี่ยวกับอนาคตได้ แต่เพียงต้องการลองใช้พวกเขาจากนั้นข้ามไปยังจุดสิ้นสุดเพื่อเล่นกับการสาธิต Mandelbrot โดยใช้การประเมินทั้งคู่ขนานและไม่ใช่แบบขนาน
ฟิวเจอร์สสามารถสร้างได้ทั้ง โดยปริยาย หรือ โดยชัดแจ้ง ในตัวอย่างเบื้องต้นข้างต้นเราใช้ ฟิวเจอร์สโดยนัย ที่สร้างขึ้นผ่านการสร้าง v %<-% { expr }
อีกทางเลือกหนึ่งคือ ฟิวเจอร์สที่ชัดเจน โดยใช้ f <- future({ expr })
และ v <- value(f)
สร้าง ด้วยสิ่งเหล่านี้ตัวอย่างของเราอาจเขียนเป็น:
> library( future )
> f <- future({
+ cat( " Hello world! n " )
+ 3.14
+ })
> v <- value( f )
Hello world !
> v
[ 1 ] 3.14
รูปแบบของการสร้างในอนาคตทำงานได้อย่างเท่าเทียมกัน (*) ดี สไตล์โดยนัยนั้นคล้ายกับการเขียนโค้ด R ปกติ โดยหลักการแล้วสิ่งที่คุณต้องทำคือแทนที่ <-
ด้วย %<-%
เพื่อเปลี่ยนการมอบหมายเป็นการมอบหมายในอนาคต ในทางกลับกันความเรียบง่ายนี้ยังสามารถหลอกลวงได้โดยเฉพาะอย่างยิ่งเมื่อมีการใช้ฟิวเจอร์สแบบอะซิงโครนัส ในทางตรงกันข้ามสไตล์ที่ชัดเจนทำให้ชัดเจนยิ่งขึ้นว่ามีการใช้ฟิวเจอร์สซึ่งจะช่วยลดความเสี่ยงต่อความผิดพลาดและสื่อสารการออกแบบให้ผู้อื่นอ่านรหัสของคุณได้ดีขึ้น
(*) มีกรณีที่ %<-%
ไม่สามารถใช้งานได้โดยไม่ต้องมีการดัดแปลง (เล็ก) เราจะกลับไปที่ข้อ จำกัด ในส่วนนี้เมื่อใช้ฟิวเจอร์สโดยปริยาย 'ใกล้ถึงจุดสิ้นสุดของเอกสารนี้
เพื่อสรุปสำหรับอนาคตที่ชัดเจนเราใช้:
f <- future({ expr })
- สร้างอนาคตv <- value(f)
- รับค่าของอนาคต (บล็อกหากยังไม่ได้รับการแก้ไข)สำหรับอนาคตโดยปริยายเราใช้:
v %<-% { expr }
- สร้างอนาคตและสัญญากับมูลค่าของมันเพื่อให้ง่ายขึ้นเราจะใช้สไตล์โดยนัยในส่วนที่เหลือของเอกสารนี้ แต่ทุกอย่างที่กล่าวถึงจะนำไปใช้กับอนาคตที่ชัดเจน
แพ็คเกจในอนาคตใช้ฟิวเจอร์ประเภทต่อไปนี้:
ชื่อ | OSE | คำอธิบาย |
---|---|---|
ซิงโครนัส: | ไม่ใช่แบบขนาน: | |
sequential | ทั้งหมด | ตามลำดับและในกระบวนการ r ปัจจุบัน |
อะซิงโครนัส: | ขนาน : | |
multisession | ทั้งหมด | พื้นหลัง r เซสชัน (บนเครื่องปัจจุบัน) |
multicore | ไม่ใช่ windows/ไม่ใช่ rstudio | กระบวนการ R Forked R (บนเครื่องปัจจุบัน) |
cluster | ทั้งหมด | เซสชัน R ภายนอกในเครื่องจักรปัจจุบันท้องถิ่นและ/หรือระยะไกล |
แพ็คเกจในอนาคตได้รับการออกแบบเพื่อให้การสนับสนุนกลยุทธ์เพิ่มเติมสามารถนำไปใช้ได้เช่นกัน ตัวอย่างเช่นแพ็คเกจ Future.callr ให้แบ็กเอนด์ในอนาคตที่ประเมินอนาคตในกระบวนการพื้นหลัง R โดยใช้แพ็คเกจ Callr - พวกเขาทำงานคล้ายกับฟิวเจอร์ส multisession
แต่มีข้อได้เปรียบเล็กน้อย ต่อเนื่องแพ็คเกจ Batchtools ให้บริการฟิวเจอร์สสำหรับ ฟังก์ชั่นคลัสเตอร์ ทุกประเภท ("แบ็กเอนด์") ที่แพ็คเกจ Batchtools รองรับ โดยเฉพาะฟิวเจอร์สสำหรับการประเมินการแสดงออกของ R ผ่านตัวกำหนดตารางงานเช่น Slurm, Torque/PBS, เครื่องยนต์ Oracle/Sun Grid (SGE) และ Load Sharing Facility (LSF)
โดยค่าเริ่มต้นการแสดงออกในอนาคตจะได้รับการประเมินอย่างกระตือรือร้น (= ทันที) และซิงโครนัส (ในเซสชัน R ปัจจุบัน) กลยุทธ์การประเมินนี้เรียกว่า "ลำดับ" ในส่วนนี้เราจะผ่านกลยุทธ์เหล่านี้แต่ละข้อและหารือเกี่ยวกับสิ่งที่พวกเขามีเหมือนกันและพวกเขาแตกต่างกันอย่างไร
ก่อนที่จะผ่านกลยุทธ์ในอนาคตที่แตกต่างกันแต่ละครั้งอาจมีประโยชน์ในการชี้แจงวัตถุประสงค์ของ API ในอนาคต (ตามที่กำหนดโดยแพ็คเกจในอนาคต) เมื่อการเขียนโปรแกรมด้วยฟิวเจอร์สไม่ควรสำคัญว่าจะใช้กลยุทธ์ในอนาคตใดสำหรับการดำเนินการโค้ด นี่เป็นเพราะเราไม่สามารถรู้ได้ว่าทรัพยากรการคำนวณที่ผู้ใช้สามารถเข้าถึงได้ดังนั้นการเลือกกลยุทธ์การประเมินควรอยู่ในมือของผู้ใช้ไม่ใช่นักพัฒนา กล่าวอีกนัยหนึ่งรหัสไม่ควรตั้งสมมติฐานใด ๆ เกี่ยวกับประเภทของอนาคตที่ใช้เช่นซิงโครนัสหรือแบบอะซิงโครนัส
หนึ่งในการออกแบบของ API ในอนาคตคือการห่อหุ้มความแตกต่างใด ๆ ที่ฟิวเจอร์สทุกประเภทจะทำงานเหมือนกัน แม้จะมีการแสดงออกอาจได้รับการประเมินในท้องถิ่นในเซสชั่น R ปัจจุบันหรือทั่วโลกในช่วงระยะไกล R ข้อได้เปรียบที่ชัดเจนอีกประการหนึ่งของการมี API และพฤติกรรมที่สอดคล้องกันระหว่างฟิวเจอร์สที่แตกต่างกันคือมันช่วยในขณะที่การสร้างต้นแบบ โดยทั่วไปแล้วจะใช้การประเมินตามลำดับในขณะที่สร้างสคริปต์และต่อมาเมื่อสคริปต์ได้รับการพัฒนาอย่างเต็มที่หนึ่งอาจเปิดการประมวลผลแบบอะซิงโครนัส
ด้วยเหตุนี้ค่าเริ่มต้นของกลยุทธ์ที่แตกต่างกันจึงเป็นเช่นนั้นผลลัพธ์และผลข้างเคียงของการประเมินการแสดงออกในอนาคตนั้นคล้ายกันมากที่สุด โดยเฉพาะอย่างยิ่งต่อไปนี้เป็นจริงสำหรับฟิวเจอร์สทั้งหมด:
การประเมินทั้งหมดจะทำในสภาพแวดล้อมในท้องถิ่น (เช่น local({ expr })
) ดังนั้นการมอบหมายจะไม่ส่งผลกระทบต่อสภาพแวดล้อมการโทร นี่เป็นธรรมชาติเมื่อประเมินในกระบวนการ R ภายนอก แต่ยังมีการบังคับใช้เมื่อประเมินในเซสชัน R ปัจจุบัน
เมื่อมีการสร้างอนาคต ตัวแปรทั่วโลกจะถูกระบุ สำหรับการประเมินแบบอะซิงโครนัส Globals จะถูกส่งออกไปยังกระบวนการ/เซสชัน R ที่จะประเมินการแสดงออกในอนาคต สำหรับฟิวเจอร์สตามลำดับที่มีการประเมินขี้เกียจ ( lazy = TRUE
), Globals คือ "Frozen" (โคลนกับสภาพแวดล้อมท้องถิ่นในอนาคต) นอกจากนี้เพื่อป้องกันการส่งออกวัตถุที่มีขนาดใหญ่เกินไปโดยไม่ได้ตั้งใจมีการยืนยันในตัวว่าขนาดทั้งหมดของ globals ทั้งหมดน้อยกว่าเกณฑ์ที่กำหนด (ควบคุมได้ผ่านตัวเลือก cf. help("future.options")
). หากเกินขีด จำกัด ข้อผิดพลาดของข้อมูลจะถูกโยนลงไป
การแสดงออกในอนาคตจะได้รับการประเมินเพียงครั้งเดียว ทันทีที่มีการรวบรวมค่า (หรือข้อผิดพลาด) มันจะพร้อมใช้งานสำหรับคำขอที่ประสบความสำเร็จทั้งหมด
นี่คือตัวอย่างที่แสดงให้เห็นว่าการมอบหมายทั้งหมดจะทำกับสภาพแวดล้อมในท้องถิ่น:
> plan( sequential )
> a <- 1
> x % <- % {
+ a <- 2
+ 2 * a
+ }
> x
[ 1 ] 4
> a
[ 1 ] 1
ตอนนี้เราพร้อมที่จะสำรวจกลยุทธ์ในอนาคตที่แตกต่างกัน
ฟิวเจอร์สซิงโครนัสได้รับการแก้ไขหลังจากนั้นอีกครั้งและโดยทั่วไปแล้วกระบวนการ R ที่สร้างขึ้น เมื่ออนาคตแบบซิงโครนัสได้รับการแก้ไขจะบล็อกกระบวนการหลักจนกว่าจะได้รับการแก้ไข
ลำดับอนาคตเป็นค่าเริ่มต้นเว้นแต่จะระบุไว้เป็นอย่างอื่น พวกเขาได้รับการออกแบบมาให้ทำงานใกล้เคียงกับการประเมิน R ปกติในขณะที่ยังคงเติมเต็ม API ในอนาคตและพฤติกรรมของมัน นี่คือตัวอย่างที่แสดงคุณสมบัติของพวกเขา:
> 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
เนื่องจากการประเมินผลตามลำดับเกิดขึ้นแต่ละฟิวเจอร์สทั้งสามจะได้รับการแก้ไขทันทีในช่วงเวลาที่มันถูกสร้างขึ้น โปรดทราบว่า pid
ในสภาพแวดล้อมการโทรซึ่งได้รับการกำหนดรหัสกระบวนการของกระบวนการปัจจุบันนั้นไม่ได้ถูกเขียนทับหรือลบออก นี่เป็นเพราะฟิวเจอร์สได้รับการประเมินในสภาพแวดล้อมท้องถิ่น เนื่องจากการประมวลผลแบบซิงโครนัส (UNI-) ถูกนำมาใช้ในอนาคต b
ได้รับการแก้ไขโดยกระบวนการ R หลัก (ยังอยู่ในสภาพแวดล้อมท้องถิ่น) ซึ่งเป็นเหตุผลว่าทำไมค่าของ b
และ pid
จึงเหมือนกัน
ต่อไปเราจะหันไปใช้ฟิวเจอร์สแบบอะซิงโครนัสซึ่งเป็นอนาคตที่ได้รับการแก้ไขในพื้นหลัง จากการออกแบบฟิวเจอร์สเหล่านี้ไม่ปิดกั้นนั่นคือหลังจากถูกสร้างขึ้นกระบวนการโทรพร้อมใช้งานสำหรับงานอื่น ๆ รวมถึงการสร้างอนาคตเพิ่มเติม มันก็ต่อเมื่อกระบวนการโทรพยายามเข้าถึงมูลค่าของอนาคตที่ยังไม่ได้รับการแก้ไขหรือพยายามสร้างอนาคตแบบอะซิงโครนัสอีกครั้งเมื่อกระบวนการ R ที่มีอยู่ทั้งหมดกำลังยุ่งอยู่กับการให้บริการฟิวเจอร์สอื่น ๆ
เราเริ่มต้นด้วยฟิวเจอร์สหลายครั้งเพราะพวกเขาได้รับการสนับสนุนจากระบบปฏิบัติการทั้งหมด อนาคตที่หลากหลายจะถูกประเมินในเซสชันพื้นหลัง R ที่ทำงานบนเครื่องเดียวกันกับกระบวนการเรียก R นี่คือตัวอย่างของเราที่มีการประเมินผลหลายครั้ง:
> 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
สิ่งแรกที่เราสังเกตคือค่าของ a
, c
และ pid
นั้นเหมือนกับก่อนหน้านี้ อย่างไรก็ตามเราสังเกตเห็นว่า b
แตกต่างจากก่อนหน้านี้ นี่เป็นเพราะอนาคต b
ได้รับการประเมินในกระบวนการ R ที่แตกต่างกันดังนั้นจึงส่งคืนรหัสกระบวนการที่แตกต่างกัน
เมื่อมีการใช้การประเมินผลหลายครั้งแพ็คเกจจะเปิดชุดเซสชัน R ในพื้นหลังที่จะให้บริการฟิวเจอร์สแบบหลายครั้งโดยการประเมินการแสดงออกของพวกเขาเมื่อพวกเขาสร้างขึ้น หากเซสชันพื้นหลังทั้งหมดกำลังยุ่งอยู่กับการให้บริการอนาคตอื่น ๆ การสร้างอนาคตที่หลากหลายครั้งต่อไปจะ ถูกบล็อก จนกว่าจะมีเซสชั่นพื้นหลังอีกครั้ง จำนวนกระบวนการพื้นหลังทั้งหมดที่เปิดตัวถูกตัดสินโดยค่าของ availableCores()
เช่น
> availableCores()
mc.cores
2
ผลลัพธ์เฉพาะนี้บอกเราว่าตัวเลือก mc.cores
ได้รับการตั้งค่าเพื่อให้เราได้รับอนุญาตให้ใช้ในกระบวนการทั้งหมดสอง (2) กระบวนการรวมถึงกระบวนการหลัก กล่าวอีกนัยหนึ่งด้วยการตั้งค่าเหล่านี้จะมีกระบวนการพื้นหลังสอง (2) กระบวนการที่ให้บริการอนาคตหลายครั้ง availableCores()
ยังมีความคล่องตัวต่อตัวเลือกที่แตกต่างกันและตัวแปรสภาพแวดล้อมของระบบ ตัวอย่างเช่นหากใช้ตัวกำหนดตารางเวลาการคำนวณ (เช่นแรงบิด/PBS และ Slurm) พวกเขาตั้งค่าตัวแปรสภาพแวดล้อมเฉพาะที่ระบุจำนวนแกนที่ได้รับการจัดสรรให้กับงานใด ๆ availableCores()
ยอมรับสิ่งเหล่านี้เช่นกัน หากไม่มีการระบุสิ่งอื่นใดจะมีการใช้แกนที่มีอยู่ทั้งหมดในเครื่อง parallel::detectCores()
สำหรับรายละเอียดเพิ่มเติมโปรดดู help("availableCores", package = "parallelly")
ในระบบปฏิบัติการที่ R สนับสนุน การฟอร์กร ของกระบวนการซึ่งโดยทั่วไปแล้วระบบปฏิบัติการทั้งหมดยกเว้น Windows ซึ่งเป็นทางเลือกในการวางไข่เซสชัน R ในพื้นหลังคือการแยกกระบวนการ R ที่มีอยู่ หากต้องการใช้ฟิวเจอร์สมัลติคอร์เมื่อได้รับการสนับสนุนระบุ:
plan( multicore )
เช่นเดียวกับสำหรับฟิวเจอร์สแบบหลายครั้งจำนวนสูงสุดของกระบวนการขนานที่ทำงานจะถูกตัดสินโดย availableCores()
เนื่องจากในทั้งสองกรณีการประเมินจะทำบนเครื่องท้องถิ่น
การใช้กระบวนการ R สามารถเร็วกว่าการทำงานกับเซสชัน R แยกต่างหากที่ทำงานในพื้นหลัง เหตุผลหนึ่งคือค่าใช้จ่ายของการส่งออกรอบใหญ่ไปยังเซสชั่นพื้นหลังอาจมากกว่าเมื่อฟอร์กและดังนั้นจึงใช้หน่วยความจำที่ใช้ร่วมกัน ในทางกลับกันหน่วยความจำที่ใช้ร่วมกันจะ ถูกอ่านเท่านั้น ซึ่งหมายถึงการปรับเปลี่ยนใด ๆ กับวัตถุที่ใช้ร่วมกันโดยหนึ่งในกระบวนการที่ถูกแยก ("คนงาน") จะทำให้เกิดสำเนาโดยระบบปฏิบัติการ สิ่งนี้สามารถเกิดขึ้นได้เมื่อนักสะสม R Garbage ทำงานในหนึ่งในกระบวนการที่ถูกแยกออก
ในทางกลับกันกระบวนการ Forking ก็ถือว่าไม่เสถียรในบางสภาพแวดล้อม R ตัวอย่างเช่นเมื่อรัน R จากภายในกระบวนการ RSTUDIO FORKING อาจส่งผลให้เซสชัน R CRASHED ด้วยเหตุนี้แพ็คเกจในอนาคตจะปิดใช้งาน Multicore Futures โดยค่าเริ่มต้นเมื่อทำงานจาก RSTUDIO ดู help("supportsMulticore")
สำหรับรายละเอียดเพิ่มเติม
ฟิวเจอร์สคลัสเตอร์ประเมินการแสดงออกของคลัสเตอร์เฉพาะกิจ (ตามที่ดำเนินการโดยแพ็คเกจขนาน) ตัวอย่างเช่นสมมติว่าคุณสามารถเข้าถึงสามโหนด n1
, n2
และ n3
จากนั้นคุณสามารถใช้สิ่งเหล่านี้สำหรับการประเมินแบบอะซิงโครนัสเป็น:
> 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
กลุ่มใด ๆ ที่ parallel::makeCluster()
สามารถใช้งานได้สำหรับฟิวเจอร์สคลัสเตอร์ ตัวอย่างเช่นคลัสเตอร์ข้างต้นสามารถตั้งค่าได้อย่างชัดเจนเป็น:
cl <- parallel :: makeCluster(c( " n1 " , " n2 " , " n3 " ))
plan( cluster , workers = cl )
นอกจากนี้ยังถือว่าเป็นสไตล์ที่ดีในการปิดคลัสเตอร์ cl
เมื่อไม่จำเป็นอีกต่อไปนั่นคือการโทร parallel::stopCluster(cl)
อย่างไรก็ตามมันจะปิดตัวเองหากกระบวนการหลักถูกยกเลิก สำหรับข้อมูลเพิ่มเติมเกี่ยวกับวิธีการตั้งค่าและจัดการกลุ่มดังกล่าวดู help("makeCluster", package = "parallel")
กลุ่มที่สร้างขึ้นโดยปริยายโดยใช้ plan(cluster, workers = hosts)
โดยที่ hosts
เป็นเวกเตอร์อักขระจะถูกปิดเมื่อเซสชัน R หลักสิ้นสุดลงหรือเมื่อกลยุทธ์ในอนาคตเปลี่ยนไปเช่น plan(sequential)
โปรดทราบว่าด้วยการตั้งค่าการตรวจสอบความถูกต้องอัตโนมัติ (เช่นคู่คีย์ SSH) ไม่มีอะไรป้องกันไม่ให้เราใช้วิธีการเดียวกันสำหรับการใช้คลัสเตอร์ของเครื่องรีโมต
หากคุณต้องการเรียกใช้คนงานหลายคนในแต่ละโหนดให้ทำซ้ำชื่อโหนดหลายครั้งเช่นจำนวนคนงานที่จะทำงานบนโหนดนั้น ตัวอย่างเช่น,
> plan(cluster, workers = c(rep("n1", times = 3), "n2", rep("n3", times = 5)))
จะทำงานสามคนใน n1
, หนึ่งใน n2
และห้าใน n3
ในทั้งหมดเก้าคนงานขนานกัน
ตอนนี้เราได้พูดถึงสิ่งที่สามารถเรียกได้ว่า "โทโพโลยีแบน" ของอนาคตนั่นคือฟิวเจอร์สทั้งหมดถูกสร้างขึ้นและมอบหมายให้อยู่ในสภาพแวดล้อมเดียวกัน อย่างไรก็ตามไม่มีอะไรหยุดเราจากการใช้ "ทอพอโลยีซ้อน" ของอนาคตที่อนาคตของอนาคตอาจสร้างอนาคตของอนาคตภายในและอื่น ๆ
ตัวอย่างเช่นนี่คือตัวอย่างของฟิวเจอร์สสอง "สูงสุด" ( a
และ b
) ที่ใช้การประเมินแบบหลายครั้งและในอนาคตที่สอง ( b
) ในทางกลับกันจะใช้ฟิวเจอร์ภายในสองครั้ง:
> 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
โดยการตรวจสอบรหัสกระบวนการเราจะเห็นว่ามีกระบวนการที่แตกต่างกันสามกระบวนการที่เกี่ยวข้องสำหรับการแก้ไขฟิวเจอร์ส มีกระบวนการ R หลัก (PID 1437557) และมีสองกระบวนการที่ใช้โดย a
(PID 1437804) และ b
(PID 1437805) อย่างไรก็ตามฟิวเจอร์สทั้งสอง ( b1
และ b2
) ที่ซ้อนกันโดย b
ได้รับการประเมินโดยกระบวนการ R เดียวกับ b
นี่เป็นเพราะฟิวเจอร์สซ้อนกันใช้การประเมินตามลำดับเว้นแต่จะระบุไว้เป็นอย่างอื่น มีเหตุผลบางประการสำหรับเรื่องนี้ แต่เหตุผลหลักคือมันปกป้องเราจากการวางไข่กระบวนการพื้นหลังจำนวนมากโดยไม่ได้ตั้งใจเช่นผ่านการโทรซ้ำ
ในการระบุ โทโพโลยีการประเมิน ประเภทต่าง ๆ นอกเหนือจากระดับแรกของอนาคตที่ได้รับการแก้ไขโดยการประเมินผลหลายครั้งและระดับที่สองโดยการประเมินตามลำดับเราสามารถจัดทำรายการกลยุทธ์การประเมินผลเพื่อ plan()
ขั้นแรกให้ระบุกลยุทธ์การประเมินเช่นเดียวกับข้างต้นอย่างชัดเจนว่า:
plan( list ( multisession , sequential ))
จริง ๆ แล้วเราจะได้รับพฤติกรรมเดียวกันถ้าเราลองใช้การประเมินหลายระดับหลายระดับ
> 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
เหตุผลนี้ก็คือที่นี่เพื่อปกป้องเราจากการเปิดกระบวนการมากกว่าที่เครื่องสามารถรองรับได้ ภายในสิ่งนี้ทำได้โดยการตั้งค่า mc.cores = 1
ซึ่งฟังก์ชั่นเช่น parallel::mclapply()
จะกลับมาทำงานตามลำดับ นี่เป็นกรณีสำหรับการประเมินผลทั้งแบบหลายครั้งและแบบมัลติคอร์
ต่อเนื่องถ้าเราเริ่มต้นด้วยการประเมินตามลำดับจากนั้นใช้การประเมินผลหลายครั้งสำหรับอนาคตที่ซ้อนกันเราจะได้รับ:
> 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
ซึ่งแสดงให้เห็นอย่างชัดเจนว่า a
และ b
ได้รับการแก้ไขในกระบวนการโทร (PID 1437557) ในขณะที่ Futures ที่ซ้อนกันทั้งสอง ( b1
และ b2
) ได้รับการแก้ไขในสองกระบวนการ R แยกกัน (PIDS 1438017 และ 1438016)
ต้องบอกว่าสิ่งนี้เป็นไปได้ที่จะใช้กลยุทธ์การประเมินผลหลายครั้งที่ซ้อนกันหากเราระบุจำนวน แกน กลางที่มีอยู่ในแต่ละระดับอย่างชัดเจน ในการทำเช่นนี้เราต้อง "ปรับแต่ง" การตั้งค่าเริ่มต้นซึ่งสามารถทำได้ดังนี้:
> 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
ก่อนอื่นเราจะเห็นว่าทั้ง a
และ b
ได้รับการแก้ไขในกระบวนการที่แตกต่างกัน (PIDs 1438105 และ 1438106) กว่ากระบวนการโทร (PID 1437557) ประการที่สองอนาคตที่ซ้อนกันสอง ( b1
และ b2
) ได้รับการแก้ไขในกระบวนการ R อีกสองกระบวนการ (PIDS 1438211 และ 1438212)
สำหรับรายละเอียดเพิ่มเติมเกี่ยวกับการทำงานกับฟิวเจอร์สซ้อนและกลยุทธ์การประเมินที่แตกต่างกันในแต่ละระดับให้ดูที่ Vignette 'Futures ใน R: Topologies ในอนาคต
เป็นไปได้ที่จะตรวจสอบว่าอนาคตได้รับการแก้ไขหรือไม่โดยไม่ต้องปิดกั้น สิ่งนี้สามารถทำได้โดยใช้ฟังก์ชั่น resolved(f)
ซึ่งใช้ Future f
ในอนาคตที่ชัดเจนเป็นอินพุต หากเราทำงานกับฟิวเจอร์สโดยนัย (เช่นในตัวอย่างทั้งหมดข้างต้น) เราสามารถใช้ฟังก์ชัน f <- futureOf(a)
เพื่อดึงอนาคตที่ชัดเจนจากหนึ่งโดยนัย ตัวอย่างเช่น,
> 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
บางครั้งอนาคตไม่ใช่สิ่งที่คุณคาดหวัง หากมีข้อผิดพลาดเกิดขึ้นในขณะที่ประเมินอนาคตข้อผิดพลาดจะถูกเผยแพร่และโยนเป็นข้อผิดพลาดในสภาพแวดล้อมการโทร เมื่อมีการร้องขอค่าในอนาคต ตัวอย่างเช่นหากเราใช้การประเมินที่ขี้เกียจในอนาคตที่สร้างข้อผิดพลาดเราอาจเห็นบางอย่างเช่น
> 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
ข้อผิดพลาดจะถูกโยนทิ้งทุกครั้งที่มีการร้องขอค่านั่นคือถ้าเราพยายามรับค่าอีกครั้งจะสร้างข้อผิดพลาดเดียวกัน (และเอาต์พุต):
> a
Future ' a ' ...
Error in log( b ) : non - numeric argument to mathematical function
In addition : Warning message :
restarting interrupted promise evaluation
หากต้องการดูการโทร ครั้งสุดท้าย ในสแต็คการโทรที่ให้ข้อผิดพลาดเราสามารถใช้ฟังก์ชัน backtrace()
(*) ในอนาคตเช่น
> backtrace( a )
[[ 1 ]]
log( a )
(*) traceback()
ไม่ได้ให้ข้อมูลที่เกี่ยวข้องในบริบทของอนาคต นอกจากนี้ยังเป็นไปไม่ได้ที่จะเห็นรายการการโทร (การแสดงออกที่ประเมิน) ซึ่งนำไปสู่ข้อผิดพลาด เฉพาะการโทรที่ให้ข้อผิดพลาด (นี่เป็นเพราะข้อ จำกัด ใน tryCatch()
ที่ใช้ภายใน)
เมื่อใดก็ตามที่การนิพจน์ R จะได้รับการประเมินแบบอะซิงโครนัส (ในแบบขนาน) หรือตามลำดับผ่านการประเมินแบบขี้เกียจวัตถุทั่วโลก (หรือที่รู้จักกันในชื่อ "ฟรี") จะต้องระบุและส่งผ่านไปยังผู้ประเมิน พวกเขาจำเป็นต้องส่งผ่านอย่างที่พวกเขาเป็นในเวลาที่อนาคตถูกสร้างขึ้นเพราะสำหรับการประเมินที่ขี้เกียจ globals อาจเปลี่ยนแปลงระหว่างเมื่อมันถูกสร้างขึ้นและเมื่อได้รับการแก้ไข สำหรับการประมวลผลแบบอะซิงโครนัสเหตุผลที่ต้องระบุว่ารอบโลกคือเพื่อให้สามารถส่งออกไปยังกระบวนการที่ประเมินอนาคต
แพ็คเกจในอนาคตพยายามทำให้งานเหล่านี้เป็นไปโดยอัตโนมัติเท่าที่จะทำได้ มันทำสิ่งนี้ด้วยความช่วยเหลือของแพ็คเกจ Globals ซึ่งใช้การตรวจสอบรหัสแบบคงที่เพื่อระบุตัวแปรทั่วโลก หากมีการระบุตัวแปรทั่วโลกจะถูกจับและให้บริการในกระบวนการประเมินผล ยิ่งกว่านั้นหากทั่วโลกถูกกำหนดไว้ในแพ็คเกจแล้วทั่วโลกจะไม่ถูกส่งออก แต่จะทำให้แน่ใจว่ามีการแนบแพ็คเกจที่เกี่ยวข้องเมื่อมีการประเมินอนาคต สิ่งนี้ไม่เพียง แต่สะท้อนให้เห็นถึงการตั้งค่าเซสชัน R หลัก แต่ยังช่วยลดความจำเป็นในการส่งออก Globals ซึ่งไม่เพียง แต่ช่วยประหยัดหน่วยความจำ แต่ยังรวมถึงเวลาและแบนด์วิดท์โดยเฉพาะอย่างยิ่งเมื่อใช้โหนดการคำนวณระยะไกล
ในที่สุดก็ควรชี้แจงว่าการระบุ globals จากการตรวจสอบรหัสแบบคงที่เพียงอย่างเดียวเป็นปัญหาที่ท้าทาย มักจะมีกรณีมุมที่การระบุตัวตนของ Global โดยอัตโนมัติล้มเหลวเพื่อให้มีการระบุ global เท็จ (น้อยกว่าข้อกังวล) หรือ global ที่แท้จริงบางส่วนหายไป (ซึ่งจะส่งผลให้เกิดข้อผิดพลาดในเวลาทำงานหรืออาจเป็นผลลัพธ์ที่ผิด) Vignette 'Futures in R: ปัญหาทั่วไปเกี่ยวกับการแก้ปัญหา' ให้ตัวอย่างของกรณีทั่วไปและอธิบายวิธีการหลีกเลี่ยงพวกเขารวมถึงวิธีการช่วยแพ็คเกจเพื่อระบุ globals หรือละเว้น globals ที่ระบุอย่างผิด ๆ หากนั่นไม่เพียงพอก็เป็นไปได้เสมอที่จะระบุตัวแปรทั่วโลกด้วยตนเองด้วยชื่อของพวกเขา (เช่น globals = c("a", "slow_sum")
) หรือเป็นคู่ชื่อ (เช่น globals = list(a = 42, slow_sum = my_sum)
)
มีข้อ จำกัด อย่างหนึ่งที่มีฟิวเจอร์สโดยนัยที่ไม่มีอยู่สำหรับสิ่งที่ชัดเจน เพราะอนาคตที่ชัดเจนนั้นเหมือนกับวัตถุอื่น ๆ ใน R จึงสามารถกำหนดได้ทุกที่/ทุกสิ่ง ตัวอย่างเช่นเราสามารถสร้างหลาย ๆ อย่างในวงและกำหนดให้เป็นรายการเช่น
> 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
สิ่งนี้เป็นไป ไม่ ได้ที่จะทำเมื่อใช้ฟิวเจอร์สโดยนัย นี่เป็นเพราะผู้ประกอบการที่ได้รับมอบหมาย %<-%
ไม่สามารถ ใช้ในทุกกรณีที่สามารถใช้ผู้ดำเนินการ <-
การมอบหมายปกติได้ สามารถใช้เพื่อกำหนดค่าในอนาคตให้กับ สภาพแวดล้อม (รวมถึงสภาพแวดล้อมการโทร) เช่นเดียวกับ assign(name, value, envir)
ทำงาน อย่างไรก็ตามเราสามารถกำหนดฟิวเจอร์สโดยนัยให้กับสภาพแวดล้อมโดยใช้ ดัชนีชื่อ เช่น
> 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
ที่นี่ as.list(v)
บล็อกจนกระทั่งอนาคตทั้งหมดในสภาพแวดล้อม v
ได้รับการแก้ไขแล้ว จากนั้นค่าของพวกเขาจะถูกรวบรวมและส่งคืนเป็นรายการปกติ
หากจำเป็นต้องใช้ ดัชนีตัวเลข คุณสามารถใช้ สภาพแวดล้อมรายการ ได้ รายการสภาพแวดล้อมที่ดำเนินการโดยแพ็คเกจ ListenV เป็นสภาพแวดล้อมปกติที่มีตัวดำเนินการย่อยที่กำหนดเองทำให้สามารถจัดทำดัชนีได้เช่นเดียวกับวิธีการจัดทำดัชนีรายการ โดยการใช้สภาพแวดล้อมรายการที่เราจะใช้รายการเรายังสามารถกำหนดฟิวเจอร์สโดยนัยให้กับวัตถุที่มีลักษณะคล้ายรายการโดยใช้ดัชนีตัวเลข ตัวอย่างเช่น,
> 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
ดังที่ก่อนหน้านี้ as.list(v)
บล็อกจนกว่าอนาคตทั้งหมดจะได้รับการแก้ไข
หากต้องการดูภาพประกอบสดว่ามีการประเมินอนาคตประเภทต่าง ๆ ให้เรียกใช้การสาธิต Mandelbrot ของแพ็คเกจนี้ ก่อนอื่นลองใช้การประเมินตามลำดับ
library( future )
plan( sequential )
demo( " mandelbrot " , package = " future " , ask = FALSE )
ซึ่งคล้ายกับวิธีที่สคริปต์จะทำงานหากไม่ได้ใช้ฟิวเจอร์ส จากนั้นลองใช้การประเมินผลหลายครั้งซึ่งคำนวณระนาบ Mandelbrot ที่แตกต่างกันโดยใช้กระบวนการ R คู่ขนานที่ทำงานในพื้นหลัง พยายาม,
plan( multisession )
demo( " mandelbrot " , package = " future " , ask = FALSE )
ในที่สุดหากคุณสามารถเข้าถึงเครื่องหลายเครื่องได้คุณสามารถลองตั้งค่ากลุ่มคนงานและใช้งานได้เช่น
plan( cluster , workers = c( " n2 " , " n5 " , " n6 " , " n6 " , " n9 " ))
demo( " mandelbrot " , package = " future " , ask = FALSE )
R Package Future มีให้ใน CRAN และสามารถติดตั้งได้ใน R เป็น:
install.packages( " future " )
ในการติดตั้งเวอร์ชันก่อนวางจำหน่ายที่มีอยู่ใน Git Branch develop
บน GitHub ใช้:
remotes :: install_github( " futureverse/future " , ref = " develop " )
สิ่งนี้จะติดตั้งแพ็คเกจจากแหล่งที่มา
หากต้องการมีส่วนร่วมในแพ็คเกจนี้โปรดดูการสนับสนุน