10 คะแนน โดย spilist2 2021-10-09 | 2 ความคิดเห็น | แชร์ทาง WhatsApp

บทนำ

  • เวลาเรียกใช้ JS ในเบราว์เซอร์ มักทำงานได้เร็วเพราะเอนจิน JS ของเบราว์เซอร์ถูกปรับแต่งมาอย่างดี แต่ทุกวันนี้ก็มีการใช้ JS ในสภาพแวดล้อมอื่นมากขึ้นด้วย (serverless, เกมคอนโซล, iOS เป็นต้น)

  • WASM คือเทคโนโลยีที่ช่วยให้รัน JS ได้เร็วในรันไทม์เหล่านี้

วิธีการทำงาน

  • หากมีเอนจิน JS โค้ด JS จะถูกแปลงเป็นไบต์โค้ดผ่าน interpreter และ JIT compiler เป็นต้น

  • ในสภาพแวดล้อมที่ไม่มีเอนจิน JS จำเป็นต้องแจกจ่ายเอนจิน JS ไปพร้อมกับโค้ด แต่หากแจกจ่ายเอนจิน JS เป็นโมดูล WASM ก็จะทำให้พกพาไปใช้ได้ในหลายสภาพแวดล้อม

  • โค้ด JS จะทำงานอยู่ภายในเอนจิน JS ที่ถูกแยกไว้ภายในเอนจิน WASM

  • เอนจิน JS ที่เอนจิน WASM ใช้คือ SpiderMonkey ซึ่ง Firefox ก็ใช้ตัวนี้เช่นกัน

  • WASM ไม่สามารถสร้าง machine code ได้ด้วยตัวเอง จึงต้องผ่านการคอมไพล์ด้วย JS

  • แต่เพราะใช้ JIT ไม่ได้ ปกติแล้ว WASM จึงควรจะช้า แล้ว WASM ทำให้การรัน JS “เร็วขึ้น” ได้อย่างไรกันแน่?

ใช้ WASM ที่ไหนบ้าง

การใช้ JS บน iOS (หรือสภาพแวดล้อมที่ใช้ JIT ไม่ได้)

- เกมคอนโซล, แอป iOS ที่ไม่มีสิทธิพิเศษ, สมาร์ตทีวี ฯลฯ ใช้ JIT ไม่ได้ด้วยเหตุผลด้านความปลอดภัย

    (→ มีการพูดราวกับว่าปัญหาด้านความปลอดภัยของการคอมไพล์แบบ JIT เป็นเรื่องแน่นอนอยู่แล้ว แต่ถึงลองหาดูก็ยังไม่ค่อยเข้าใจเหตุผลชัดเจนนัก)

- ดังนั้นในที่เหล่านี้จึงต้องใช้ interpreter แต่จริง ๆ แล้วแอปที่รันบนแพลตฟอร์มแบบนี้มักทำงานยาวนานและมีโค้ดจำนวนมาก จึงควรหลีกเลี่ยงการช้าลงจากการใช้ interpreter

- ถ้าอยากใช้ JS โดยหลีกเลี่ยงปัญหาประสิทธิภาพตกจาก interpreter จะทำอย่างไร?

การใช้ JS ใน serverless

- สภาพแวดล้อม serverless มี JIT ก็จริง แต่มีปัญหาเรื่อง cold start ที่นาน ทำให้ latency สูงขึ้น (แค่โหลดเอนจินก็อย่างน้อย 5 ms)

- มีเทคนิคเพิ่มประสิทธิภาพเพื่อซ่อนเวลา cold start แต่ยิ่งชั้นเครือข่ายดีขึ้น (เช่น QUIC) ความหมายของเทคนิคเหล่านี้ก็ลดลง อีกทั้งเมื่อรันฟังก์ชัน serverless หลายตัวพร้อมกัน เทคนิคเหล่านี้ก็แทบไม่ค่อยมีประโยชน์

- อาจหลีกเลี่ยงเวลา cold start ได้ด้วยการนำ instance กลับมาใช้ซ้ำ แต่ก็หมายความว่าจะมีการแชร์สถานะระหว่างคำขอ ซึ่งกลายเป็นความเสี่ยงด้านความปลอดภัย

- ด้วยเหตุนี้ ในทางปฏิบัติจึงมีหลายกรณีที่ไม่ทำตาม best practice และยัดหลายอย่างไว้ในฟังก์ชัน serverless เดียว

- กล่าวคือ ถ้าแก้ปัญหา cold start ได้ ก็ไม่จำเป็นต้องใช้เทคนิคต่าง ๆ เพื่อหลีกเลี่ยงมัน และปัญหาหลายอย่างก็จะถูกแก้ไปด้วย
  • WASM ห่อหุ้มและแยก JS ออกไว้ โค้ดของ WASM เองก็สั้นและเรียบง่าย จึงตรวจสอบได้ง่ายและลดความเสี่ยงด้านความปลอดภัยได้ด้วย

เอนจิน JS ใช้เวลาไปมากที่ไหน

เฟสเริ่มต้น

- (การเริ่มต้นเอนจิน) ส่วนนี้เกี่ยวข้องกับ serverless เอนจินต้องเตรียมตัวเองและเพิ่มฟังก์ชัน built-in เข้าไปในสภาพแวดล้อม นี่คือหนึ่งในเหตุผลที่ cold start ของ serverless ช้า

- (การเริ่มต้นแอปพลิเคชัน) แยกวิเคราะห์ฟังก์ชันเป็นไบต์โค้ด, จัดสรรหน่วยความจำให้ตัวแปร, กำหนดค่าให้ตัวแปร

เฟสรันไทม์

- ตั้งแต่ช่วงนี้เป็นต้นไป throughput จะได้รับผลจากหลายเงื่อนไข

    - ใช้ language features แบบใดบ้าง

    - โค้ดมีพฤติกรรมที่คาดเดาได้จากมุมมองของเอนจิน JS หรือไม่

    - ใช้โครงสร้างข้อมูลประเภทใด

    - โค้ดรันนานพอที่จะได้ประโยชน์จาก optimizing compiler ของเอนจิน JS หรือไม่

การทำให้เอนจิน JS เร็วขึ้นหมายถึงการทำให้ทั้งเฟสเริ่มต้นและเฟสรันไทม์เร็วขึ้น กล่าวให้ชัดคือ ลดเวลาที่ใช้ในการเริ่มต้น และเพิ่ม throughput หรือความเร็วในการประมวลผลโค้ดระหว่างรันไทม์

ลดเวลาเริ่มต้น

  • WASM ใช้ pre-initializer ที่ชื่อ Wizer เพื่อลดเวลาเริ่มต้น (สำหรับแอปขนาดเล็ก JS on WASM เร็วกว่า JS isolate ราว 13 เท่า)

    • ในขั้นตอน build ก่อนแจกจ่ายโค้ด pre-initializer จะลองรันโค้ด JS ทั้งหมดจนถึงขั้นเริ่มต้นหนึ่งรอบ

    • ทำให้โค้ด JS ถูกเก็บเป็นไบต์โค้ดอยู่ใน linear memory ของเอนจิน JS และการจัดสรรหน่วยความจำก็เสร็จแล้ว

    • จากนั้นก็คัดลอกสิ่งนี้ไปใส่ไว้ใน data section ของ WASM ตามเดิม

  • เมื่อเอนจิน JS ถูก instantiate ก็จะเข้าถึงข้อมูลทั้งหมดใน data section ได้ หากต้องการหน่วยความจำเฉพาะส่วนก็เพียงคัดลอกจาก data section มาใช้ จึงไม่ต้องเสียเวลาเริ่มต้น และนี่จึงถูกเรียกว่า pre-initialization

  • ปัจจุบัน data section ถูกแนบไว้กับโมดูลเดียวกับเอนจิน JS แต่ในอนาคตมีแผนจะใช้ module linking เพื่อแยก data section ออกเป็นโมดูลต่างหาก ทำให้หลายแอปพลิเคชันแชร์เอนจิน JS ร่วมกันได้

  • และจริง ๆ แล้วเทคนิค pre-initialization นี้ไม่ได้จำเป็นต้องจำกัดอยู่แค่เอนจิน JS แต่เป็นแนวคิดที่ใช้ได้กับรันไทม์ใดก็ได้ เช่น Python, Ruby, Lua เป็นต้น

เพิ่ม throughput

  • หากโค้ด JS รันเพียงช่วงเวลาสั้น ๆ อยู่แล้วก็จะไม่ผ่าน JIT อยู่ดี ดังนั้น throughput ของ WASM ก็จะใกล้เคียงกับบนเบราว์เซอร์ แต่สำหรับโค้ดที่รันยาว ความต่างของ throughput จากการมีหรือไม่มี JIT จะมาก

  • WASM ใช้ JIT ไม่ได้ จึงเลือกใช้การคอมไพล์แบบ AOT (ahead-of-time) แทน โดยดึงเทคนิคที่นำมาจาก JIT ได้ก็เอามาใช้

  • หนึ่งในเทคนิคเพิ่มประสิทธิภาพของ JIT คือ inline caching คือการเก็บชิ้นส่วนโค้ดที่เคยรันไว้แล้วนำกลับมาใช้ซ้ำ

  • ใน WASM มีการเตรียมรูปแบบการใช้งานที่พบบ่อยใน JS ไว้เป็น stub เช่น การเข้าถึง property ของอ็อบเจ็กต์

    • ปกติแล้วการเข้าถึง property ของอ็อบเจ็กต์อย่างถูกต้องต้องมีข้อมูล shape และ offset ซึ่งไม่สามารถรู้ได้ล่วงหน้าด้วย AOT

    • แต่สามารถเตรียม stub ที่เข้าถึง property โดยรับ shape และ offset เป็นพารามิเตอร์ไว้ล่วงหน้าได้ และ stub นี้ก็สามารถนำกลับมาใช้ซ้ำได้หลายจุด

  • WASM เตรียม common patterns เหล่านี้ไว้เป็น stub ทั้งหมด โดยไม่เกี่ยวว่าโค้ด JS จริงมีหน้าตาอย่างไร วิธีนี้ช่วยลด machine code ที่เอนจิน JS ต้องสร้าง ลดเวลาเริ่มต้น และทำให้ cache locality ดีขึ้น

  • มีการยืนยันแล้วว่าเพียงเตรียม stub ไว้ 2kb ก็ครอบคลุมโค้ด JS จริงได้ประมาณ 95%

  • เทคนิคแบบนี้เป็นการเพิ่มประสิทธิภาพแบบ ahead-of-time กล่าวคือ ปรับให้เหมาะโดยไม่รู้เนื้อหาโค้ดล่วงหน้า (ไม่มี profiling) ดังนั้นถ้าทำ profiling เพิ่มเติม ก็ยังอาจมีพื้นที่ให้เพิ่มประสิทธิภาพได้มากขึ้นเหมือน JIT

    • แต่ตัว profiling เองก็ไม่ใช่เรื่องง่าย จึงยังอยู่ระหว่างพัฒนา

2 ความคิดเห็น

 
kunggom 2021-10-10

เกี่ยวกับประเด็นความปลอดภัยของ JIT เคยมีการกล่าวถึงเรื่องนี้ไว้ในบทความบล็อกของทีม MS Edge ที่เคยแนะนำไว้ที่นี่ก่อนหน้านี้ โดยพื้นฐานแล้วเอนจิน JIT มีความซับซ้อน จึงไม่เพียงแต่เพิ่มพื้นผิวการโจมตีเท่านั้น แต่ยังดูเหมือนว่าวิธีการอย่าง Speculative Optimization ที่ JIT ใช้เพื่อเพิ่มประสิทธิภาพ ก็มีแนวโน้มทำให้เกิดปัญหาความปลอดภัยบางรูปแบบซ้ำๆ ด้วย ด้วยเหตุนี้จึงมีการบอกว่าสัดส่วนของช่องโหว่ความปลอดภัยที่เกี่ยวข้องกับ JIT ในเว็บเบราว์เซอร์นั้นค่อนข้างสูง

https://th.news.hada.io/topic?id=4771

https://microsoftedge.github.io/edgevr/posts/Super-Duper-Secure-Mode/

https://docs.google.com/spreadsheets/d/…

 
spilist2 2021-10-10

โอ้ ขอบคุณมากครับ! แต่ผมดันไม่ได้ลองหาใน GeekNews เองเลย