การเพิ่มประสิทธิภาพโค้ดพร้อมกัน
เรียนรู้เทคนิคขั้นสูงเพื่อเพิ่มประสิทธิภาพแอปพลิเคชัน Scala ที่ทำงานพร้อมกัน รวมถึงการปรับแต่งกลุ่มเธรดและการหลีกเลี่ยงการแย่งใช้ทรัพยากร
การเพิ่มประสิทธิภาพโค้ดพร้อมกัน เป็นบทเรียน Scala for Backend Engineering & Functional Programming ฟรีบน CoddyKit นี่คือบทเรียนที่ 3 จากทั้งหมด 3 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Scala for Backend Engineering & Functional Programming และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Scala for Backend Engineering & Functional Programming มีบทเรียนทั้งหมด 3 บทเรียน
เหตุใดจึงต้องปรับปรุงโค้ดที่ทำงานพร้อมกัน
การปรับปรุงโค้ด Scala ที่ทำงานพร้อมกันเป็นสิ่งสำคัญต่อการสร้างแอปพลิเคชันที่มีประสิทธิภาพสูงและรองรับการขยายขนาดได้ดี เป้าหมายคือทำให้โปรแกรมทำงานเร็วขึ้นและจัดการงานได้มากขึ้นพร้อมกัน
- ปริมาณงาน: สามารถดำเนินการได้กี่รายการต่อหน่วยเวลา
- เวลาแฝง: การดำเนินการหนึ่งรายการใช้เวลานานเท่าใด
- การใช้ทรัพยากร: แกน CPU และหน่วยความจำถูกใช้อย่างมีประสิทธิภาพหรือไม่
โค้ดที่ทำงานพร้อมกันแต่ปรับปรุงได้ไม่ดีอาจทำให้เกิดคอขวด การหยุดชะงัก และการใช้ทรัพยากรอย่างไม่มีประสิทธิภาพ จนลบล้างประโยชน์ของการทำงานพร้อมกัน
พูลเธรดทำงานอย่างไร
พูลเธรด คือกลุ่มเธรดผู้ปฏิบัติงานที่เตรียมไว้ล่วงหน้าและนำกลับมาใช้ซ้ำเพื่อดำเนินงาน แทนที่จะสร้างเธรดใหม่สำหรับแต่ละงาน ซึ่งมีค่าใช้จ่ายสูง งานต่าง ๆ จะถูกส่งไปยังพูล
วิธีนี้ช่วยลดค่าใช้จ่ายในการสร้างและทำลายเธรด เพิ่มการตอบสนอง และช่วยจัดการจำนวนเธรดที่ทำงานอยู่เพื่อป้องกันการใช้ทรัพยากรมากเกินไป
กำหนดค่าพูลส่วนกลางของ Scala
ใน Scala การดำเนินการแบบอะซิงโครนัสมักใช้ ExecutionContext โดยค่าเริ่มต้น แอปพลิเคชัน Scala จะใช้ ExecutionContext ส่วนกลาง ซึ่งโดยทั่วไปคือ ForkJoinPool
คุณสามารถปรับแต่งพูลเริ่มต้นนี้ได้โดยกำหนดคุณสมบัติของระบบ หรือโดยทั่วไปกว่านั้นคือกำหนดการตั้งค่าดิสแพตเชอร์ใน application.conf โดยเฉพาะในแอปพลิเคชันที่ใช้ Akka วิธีนี้ช่วยให้คุณควบคุมจำนวนเธรด พฤติกรรมการขโมยงาน และขนาดคิวได้
การตั้งค่าที่มักปรับคือ parallelism-factor เพื่อปรับขนาดพูลให้สัมพันธ์กับจำนวนแกน CPU ที่พร้อมใช้งาน
สร้าง ExecutionContext แบบกำหนดเอง
แม้ ExecutionContext ส่วนกลางจะสะดวก แต่บ่อยครั้งการสร้างพูลเธรดแบบกำหนดเองสำหรับงานแต่ละประเภทจะเหมาะสมกว่า ตัวอย่างเช่น ใช้พูลขนาดเล็กสำหรับงานที่ใช้ CPU และพูลขนาดใหญ่กว่าสำหรับงานที่ใช้การรับส่งข้อมูลเข้าออก
วิธีนี้ช่วยป้องกันไม่ให้การดำเนินการรับส่งข้อมูลที่ช้าขัดขวางงานที่ใช้ CPU และช่วยให้ระบบตอบสนองโดยรวมดีขึ้น ต่อไปนี้คือวิธีสร้าง ExecutionContext แบบกำหนดเองอย่างง่าย:
import java.util.concurrent.{Executors, ExecutorService}
import scala.concurrent.ExecutionContext
object CustomPoolExample {
def main(args: Array[String]): Unit = {
// Create a fixed thread pool with 4 threads
val customExecutor: ExecutorService =
Executors.newFixedThreadPool(4)
// Wrap it in a Scala ExecutionContext
implicit val customEC: ExecutionContext =
ExecutionContext.fromExecutor(customExecutor)
println("Custom ExecutionContext created.")
// Don't forget to shut down the executor!
customExecutor.shutdown()
}
}ลดการแย่งชิงทรัพยากร
การแย่งชิง เกิดขึ้นเมื่อมีหลายเธรดพยายามเข้าถึงทรัพยากรที่ใช้ร่วมกัน เช่น ตัวแปร โครงสร้างข้อมูล หรือการเชื่อมต่อฐานข้อมูล ในเวลาเดียวกัน และมีเธรดอย่างน้อยหนึ่งเธรดต้องรอ
การรอนี้ทำให้เกิดความล่าช้าและค่าใช้จ่ายเพิ่มเติม เนื่องจากเธรดต้องแข่งขันกันใช้ล็อกและรอบการทำงานของ CPU การแย่งชิงที่สูงอาจทำให้ประสิทธิภาพของแอปพลิเคชันที่ทำงานพร้อมกันลดลงอย่างมาก แม้จะมีแกน CPU ให้ใช้งานหลายแกน
กลยุทธ์ในการลดการแย่งชิงเป็นกุญแจสำคัญในการดึงศักยภาพของการทำงานแบบขนานออกมาอย่างแท้จริง
การล็อกแบบละเอียด
แทนที่จะใช้ล็อกขนาดหยาบเพียงตัวเดียวเพื่อป้องกันออบเจ็กต์ทั้งหมดหรือบล็อกโค้ดขนาดใหญ่ การล็อกแบบละเอียด จะใช้ล็อกขนาดเล็กและเฉพาะเจาะจงมากขึ้น เพื่อป้องกันเฉพาะส่วนของข้อมูลที่กำลังถูกแก้ไขจริง ๆ
วิธีนี้ทำให้เธรดต่าง ๆ สามารถเข้าถึงส่วนต่าง ๆ ของออบเจ็กต์พร้อมกันได้ จึงลดการแย่งชิงลงอย่างมาก อย่างไรก็ตาม วิธีนี้เพิ่มความซับซ้อนและความเสี่ยงที่จะเกิดการหยุดชะงัก หากจัดการไม่รอบคอบ
class Counter {
private var value = 0
private val lock = new Object() // A specific lock for 'value'
def increment(): Unit = lock.synchronized {
value += 1
}
def get(): Int = lock.synchronized {
value
}
}
object FineGrainedLocking {
def main(args: Array[String]): Unit = {
val counter = new Counter()
println(s"Initial counter value: ${counter.get()}")
counter.increment()
println(s"Incremented value: ${counter.get()}")
}
}การดำเนินการแบบอะตอมิกเพื่อประสิทธิภาพ
โครงสร้างข้อมูลที่ไม่ใช้ล็อก ใช้การดำเนินการระดับต่ำแบบอะตอมิก เช่น Compare-And-Swap หรือ CAS เพื่ออัปเดตตัวแปรที่ใช้ร่วมกันโดยไม่ต้องใช้ล็อกโดยตรง วิธีนี้ช่วยหลีกเลี่ยงค่าใช้จ่ายและการแย่งชิงที่อาจเกิดจากกลไกการล็อกแบบดั้งเดิม
Scala ใช้แพ็กเกจ java.util.concurrent.atomic ของ Java สำหรับงานนี้ คลาสต่าง ๆ เช่น AtomicInteger, AtomicLong และ AtomicReference มีการอัปเดตแบบอะตอมิก จึงเหมาะสำหรับสถานการณ์ที่มีการแย่งชิงสูง
import java.util.concurrent.atomic.AtomicInteger
object AtomicCounterExample {
def main(args: Array[String]): Unit = {
val atomicCounter = new AtomicInteger(0)
// Increment the counter atomically
atomicCounter.incrementAndGet()
println(s"Atomic counter after increment: ${atomicCounter.get()}")
// Another atomic operation: add 5
atomicCounter.addAndGet(5)
println(s"Atomic counter after adding 5: ${atomicCounter.get()}")
}
}วัดประสิทธิภาพอย่างแม่นยำ
หากต้องการทราบอย่างแท้จริงว่าการปรับปรุงโค้ดที่ทำงานพร้อมกันได้ผลหรือไม่ คุณต้องวัดผลอย่างแม่นยำ การจับเวลาอย่างง่ายด้วย System.nanoTime() มักไม่เพียงพอสำหรับโค้ดที่ทำงานพร้อมกัน เนื่องจากการปรับปรุงของ JVM ช่วงวอร์มอัป และการสลับบริบท
เครื่องมือวัดประสิทธิภาพระดับมืออาชีพอย่าง JMH (Java Microbenchmark Harness) ได้รับการออกแบบมาเพื่อจุดประสงค์นี้ เครื่องมือจะจัดการช่วงวอร์มอัปของ JVM การกำจัดโค้ดที่ไม่มีผล และให้การวิเคราะห์ทางสถิติ ทำให้ได้ตัวชี้วัดประสิทธิภาพที่น่าเชื่อถือสำหรับอัลกอริทึมที่ทำงานพร้อมกัน
ให้มุ่งเน้นตัวชี้วัด เช่น จำนวนการดำเนินการต่อวินาที (ปริมาณงาน) และเวลาเฉลี่ยในการดำเนินการ (เวลาแฝง) ภายใต้เงื่อนไขปริมาณงานที่แตกต่างกัน
ตรวจสอบการปรับปรุงการทำงานพร้อมกัน
ข้อใดต่อไปนี้เป็นกลยุทธ์ที่มีประสิทธิภาพในการปรับปรุงแอปพลิเคชัน Scala ที่ทำงานพร้อมกันและลดการแย่งชิง
ทบทวน: ประสิทธิภาพการทำงานพร้อมกันที่ดียิ่งขึ้น
ในบทเรียนนี้ เราได้สำรวจเทคนิคขั้นสูงสำหรับการปรับปรุงแอปพลิเคชัน Scala ที่ทำงานพร้อมกัน:
- เราได้เรียนรู้ความสำคัญของการปรับแต่งพูลเธรด ซึ่งรวมถึงการกำหนดค่า
ExecutionContextเริ่มต้นของ Scala และการสร้างพูลแบบกำหนดเองสำหรับงานแต่ละประเภท - เราได้พูดถึงกลยุทธ์การหลีกเลี่ยงการแย่งชิง เช่น การลดความละเอียดของล็อกด้วยการล็อกแบบละเอียด
- เราได้เห็นว่าโครงสร้างข้อมูลที่ไม่ใช้ล็อก โดยเฉพาะตัวแปรแบบอะตอมิก สามารถอัปเดตสถานะที่ใช้ร่วมกันได้อย่างมีประสิทธิภาพและมีค่าใช้จ่ายต่ำ
โปรดจำไว้ว่า ควรวัดผลกระทบจากการปรับปรุงของคุณด้วยเครื่องมือวัดประสิทธิภาพที่เหมาะสมเสมอ เพื่อให้มั่นใจว่าได้ประสิทธิภาพเพิ่มขึ้นจริง
คำถามที่พบบ่อย
บทเรียน “การเพิ่มประสิทธิภาพโค้ดพร้อมกัน” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “การเพิ่มประสิทธิภาพโค้ดพร้อมกัน” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Scala for Backend Engineering & Functional Programming ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Scala for Backend Engineering & Functional Programming มีบทเรียนทั้งหมด 3 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “การเพิ่มประสิทธิภาพโค้ดพร้อมกัน”
เรียนรู้เทคนิคขั้นสูงเพื่อเพิ่มประสิทธิภาพแอปพลิเคชัน Scala ที่ทำงานพร้อมกัน รวมถึงการปรับแต่งกลุ่มเธรดและการหลีกเลี่ยงการแย่งใช้ทรัพยากร คุณปฏิบัติ Scala for Backend Engineering & Functional Programming ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Scala for Backend Engineering & Functional Programming หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน Scala for Backend Engineering & Functional Programming บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 3 จากทั้งหมด 3 บทเรียน
บทเรียน “การเพิ่มประสิทธิภาพโค้ดพร้อมกัน” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน Scala for Backend Engineering & Functional Programming นี้ได้ไหม
ได้ บทเรียน Scala for Backend Engineering & Functional Programming ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- การวิเคราะห์ประสิทธิภาพแอปพลิเคชัน Scala
- การจัดการหน่วยความจำและการปรับแต่ง GC
- การเพิ่มประสิทธิภาพโค้ดพร้อมกัน