ข้าม​ไป​ยัง​เนื้อหา

Amdahl’s Law

มี​เพดาน​ทาง​ทฤษฎี​ว่าการ​ทำงาน​แบบ​ขนาน​จะ​เร่ง​ความเร็ว​ได้​แค่​ไหน

Amdahl’s Law ตั้ง​ชื่อ​ตาม Gene Amdahl วิศวกร​คอมพิวเตอร์​ผู้​ออกแบบ​เมน frame ของ IBM ที่​เสนอ​แนวคิด​นี้​ใน​ปี 1967 ที่​งาน AFIPS Spring Joint Computer Conference เพื่อ​โต้แย้ง​ข้อ​ถกเถียง​ใน​ยุค​นั้น​ที่​มอง​ว่าการ​เพิ่ม​จำนวน processor จะ​ทำให้​ระบบ​เร็ว​ขึ้น​แบบ​ไม่มี​ขีด​จำกัด บริบท​ของ​ยุค​นั้น​คือ​การ​ถกเถียง​กัน​ใน​วงการ​ว่า​ระบบ massively-parallel อย่าง ILLIAC IV จะ​เป็น​อนาคต​ของ​การ​ประมวล​ผล​หรือ​ไม่ Amdahl ใช้​สูตร​ทาง​คณิตศาสตร์​ง่าย ๆ นี้​เพื่อ​ชี้​ให้​เห็น​ว่า​ฮาร์ดแวร์​ที่​ทำ​ขนาน​ได้​มหาศาล​ก็​ยัง​มี​ขีด​จำกัด​จาก​ส่วน​ของ​โปรแกรม​ที่​ยัง​ต้อง​ทำงาน​แบบ serial อยู่ดี

กฎ​นี้​เขียน​เป็น​สูตร​ได้​ว่า

Speedup(N) = 1 / ( (1 − P) + P/N )

โดยที่ P คือ​สัดส่วน​ของ​งาน​ทั้งหมด​ที่​สามารถ​ทำ​แบบ​ขนาน​ได้ (parallelizable) (1 − P) คือ​สัดส่วน​ที่​ต้อง​ทำ​แบบ serial เท่านั้น และ N คือ​จำนวน processor หรือ​หน่วย​ประมวล​ผล​ที่​ใช้​ทำงาน​ส่วน​ที่​ขนาน​ได้

แก่น​ของ​กฎ​คือ: ไม่​ว่า​จะ​ทุ่ม N มาก​เท่า​ไหร่ ผลลัพธ์​ของ​สูตร​ก็​ไม่มี​วัน​เกิน 1 / (1 − P) เพราะ​เมื่อ N เข้า​ใกล้​อนันต์ พจน์ P/N จะ​เข้า​ใกล้​ศูนย์ เหลือ​แต่​ส่วน serial ที่​ตรึง​เพดาน​ไว้​ตลอด นี่​คือ​เหตุผล​ที่ “คอ​ขวด” (bottleneck) ที่​ทำ​ขนาน​ไม่​ได้ ไม่​ว่า​จะ​เล็ก​แค่​ไหน ก็​เป็น​ตัว​กำหนด​ผลลัพธ์​สุดท้าย​ของ​ทั้ง​ระบบ

ตัวเลข​ที่​ทำให้​เห็น​ภาพ​ชัด​คือ​การ​ลอง​แทน​ค่า สมมติ​มี​งาน​ที่ 10% ของ​เวลา​ทั้งหมด​ถูก​เร่ง​ความเร็ว​ได้​ถึง 10,000 เท่า (แทบ​จะ​เป็น​ศูนย์​วินาที) ส่วน​ที่​เหลือ​อีก 90% ยัง​คง​ทำงาน​แบบ serial เหมือน​เดิม ผลลัพธ์​สุดท้าย​คือ​ระบบ​เร็ว​ขึ้น​เพียง​ประมาณ 11% เท่านั้น — ไม่ใช่ 10,000 เท่า​อย่าง​ที่​หลาย​คน​คาด​หวัง ใน​ทาง​กลับ​กัน ถ้า​เพิ่ม speed ให้ 99% ของ​งาน​ด้วย​ตัว​เร่ง 100 เท่า ระบบ​โดย​รวม​จะ​เร็ว​ขึ้น​ได้​สูงสุด​ประมาณ 50 เท่า เพราะ 1% ที่​เหลือกลาย​เป็น​เพดาน​ใหม่

นัย​ยะ​สำคัญ​มี​สาม​ข้อ

  1. ผล​ตอบแทน​ลด​ลง​เรื่อย ๆ (diminishing returns) — การ​เพิ่ม processor หรือ worker เข้าไป​เรื่อย ๆ ให้​ผล​ตอบแทน​ที่​น้อย​ลง​ทุก​ครั้ง เพราะ​เพดาน​ถูก​กำหนด​โดย​สัดส่วน serial ไม่ใช่​โดย​จำนวน N
  2. ต้อง​รู้​ก่อน​ว่า​อะไร​คือ serial — ก่อน​จะ​ลงทุน​ขนาน​งาน ต้อง​วัด​ให้​ได้​ก่อน​ว่า​สัดส่วน P จริง ๆ คือ​เท่า​ไหร่ เพราะ​การ​เดา​ผิด​ทำให้​ทุ่ม​ทรัพยากร​ผิด​จุด
  3. สมมติฐาน​คือ workload คงที่ — Amdahl’s Law มอง​ปัญหา​ขนาด​คงที่ (fixed problem size) ซึ่ง​ต่าง​จาก Gustafson’s Law ที่​มอง​ว่า​เมื่อ​มี processor มาก​ขึ้น คน​มัก​จะ​ขยาย​ขนาด​งาน​ตาม​ไป​ด้วย (เช่น ประมวล​ผล dataset ที่​ใหญ่​ขึ้น​ใน​เวลา​เท่า​เดิม) ทำให้​สัดส่วน serial ต่อ workload ทั้งหมด​ลด​ลง​เมื่อ scale ขึ้น สอง​กฎ​นี้​ไม่​ได้​ขัดแย้ง​กัน แต่​ตอบ​คำถาม​คนละ​แบบ — Amdahl’s Law เหมาะ​กับ​คำถาม “งาน​เดิม​จะ​เร็ว​ขึ้น​แค่​ไหน” ส่วน Gustafson’s Law เหมาะ​กับ​คำถาม “งาน​ที่​ใหญ่​ขึ้น​จะ​เสร็จ​ใน​เวลา​เท่า​เดิม​ได้​แค่​ไหน”

ใน​ทาง​ปฏิบัติ ระบบ​จริง​มัก​มี​เพดาน​ที่​ต่ำ​กว่า​ที่​สูตร​ทำนาย​ไว้​ด้วย​ซ้ำ เพราะ​ยัง​มี​คอ​ขวด​อื่น​ที่ Amdahl’s Law ไม่​ได้​รวม​ไว้ เช่น memory bandwidth, I/O bandwidth, network latency และ overhead ของ​การ​ประสาน​งาน​ระหว่าง thread/process (contention, locking, context switching)

อีก​คำ​ที่​มัก​ใช้​คู่​กัน​ใน​วงการ HPC (high-performance computing) คือ strong scaling ซึ่ง​หมาย​ถึง​การ​วัด speedup เมื่อ​เพิ่ม processor โดย​ขนาด​ปัญหา​คงที่ — นี่​คือ​สิ่ง​ที่ Amdahl’s Law อธิบาย​โดยตรง ตรง​ข้าม​กับ weak scaling ที่​ขนาด​ปัญหา​ขยาย​ตาม​จำนวน processor ซึ่ง​ใกล้​เคียง​กับ​มุมมอง​ของ Gustafson’s Law มากกว่า การ​แยก​สอง​คำ​นี้​ให้​ออก​ช่วย​ให้​เลือก​ใช้​สูตร​และ​ตั้ง​ความ​คาด​หวัง​ได้​ถูกต้อง​ตั้งแต่​ต้น

flowchart LR
  Start --> Serial
  Serial --> Split
  Split --> W1
  Split --> W2
  Split --> W3
  W1 --> Join
  W2 --> Join
  W3 --> Join
  Join --> Serial2
  Serial2 --> End

แผนภาพ​ข้าง​ต้น​แสดง​งาน​หนึ่ง​ชิ้น​ที่​มี​ช่วง serial สอง​ช่วง (Serial, Serial2) คั่น​หัว​ท้าย​ของ​ช่วง​ที่​ทำ​ขนาน​ได้ (Split กระจาย​งาน​ให้ W1, W2, W3 แล้ว Join รวม​ผล) ไม่​ว่า​จะ​เพิ่ม worker ใน​ช่วง​กลาง​กี่​ตัว เวลา​รวม​ของ Serial และ Serial2 ก็​ยัง​คง​เท่า​เดิม​เสมอ — นี่​คือ​ภาพ​จำลอง​ของ (1 − P) ใน​สูตร

การ​ประชุม​ทีม​ที่​รอ​คน​ปั่น​จักรยาน — ตัวอย่าง​คลาสสิก​ที่​ใช้​อธิบาย​กฎ​นี้​คือ เพื่อน​ร่วม​งาน​สาม​คน​ต้อง​ไป​ถึง​ห้อง​ประชุม​พร้อม​กัน สอง​คน​ขับ​รถ​ถึง​เร็ว อีก​คน​ปั่น​จักรยาน​มา​ช้า​กว่า ถ้า​ทีม​อยาก​เริ่ม​ประชุม​เร็ว​ขึ้น การ​ซื้อ​รถ​ที่​เร็ว​ขึ้น​ให้​สอง​คน​แรก​ไม่​ช่วย​อะไร​เลย เพราะ​ตัว​ที่​กำหนด​เวลา​เริ่ม​ประชุม​คือ​ความเร็ว​ของ​คน​ปั่น​จักรยาน — ต้อง​แก้​ที่ bottleneck ไม่ใช่​ที่​ส่วน​ที่​เร็ว​อยู่​แล้ว

เว็บไซต์ e-commerce ที่​เพิ่ม server แต่​ยัง​ช้า — ร้าน​ค้า​ออนไลน์​เพิ่ม​จำนวน application server เพื่อ​รองรับ traffic ที่​พุ่ง​ขึ้น​หลัง​เปิด​ตัว​สินค้า​ใหม่ แต่​พบ​ว่า​ความเร็ว​โดย​รวม​ยัง​ถูก​จำกัด​ด้วย​เวลา​ที่​แต่ละ​หน้า​เว็บ​ถูก serve ออก​มา (เช่น query ฐาน​ข้อมูล​ที่​ทำงาน serial หรือ third-party API ที่​ต้อง​รอ) การ​เพิ่ม server แนว​นอน​ช่วย​ได้​แค่​ใน​ส่วน​ที่​ขนาน​ได้​เท่านั้น ส่วน​ที่​เป็น​คอ​ขวด​จริง ๆ ต้อง​ได้​รับ​การ optimize โดยตรง

Microservices ที่​ยัง​ชน​กัน​ที่ shared service — ทีม1 decompose monolith เป็น microservices จำนวน​มาก​เพื่อ scale แนว​นอน แต่​ทุก request ยัง​ต้อง​ผ่าน authentication service ตัว​เดียว​ที่​เป็น serial bottleneck ไม่​ว่า​จะ​เพิ่ม instance ของ microservices อื่น​กี่​ตัว throughput รวม​ของ​ระบบ​ก็​ไม่มี​วัน​เกิน​ความ​สามารถ​ของ auth service ตัว​นั้น — สะท้อน​ให้​เห็น​ว่าการ scale สถาปัตยกรรม​ต้อง​มอง​หา serial path ที่แท้​จริง ไม่ใช่​แค่​จำนวน service

CPU multicore กับ​ซอฟต์แวร์​ที่​ไม่​รองรับ multithreading เต็ม​ที่ — การ​เพิ่ม core ให้ CPU จาก 4 เป็น 64 core ไม่​ได้​ทำให้​โปรแกรม​ที่​มี​ส่วน initialization, I/O หรือ algorithm บาง​ส่วน​ที่​เขียน​แบบ single-threaded เร็ว​ขึ้น​ตาม​สัดส่วน core ที่​เพิ่ม เพราะ​ส่วน​เหล่า​นั้น​ยัง​คง​รัน​แบบ serial เสมอ นี่​คือ​เหตุผล​ที่​ทีม hardware และ compiler ทุ่มเท​มหาศาล​กับ​การ​ลด​สัดส่วน serial code ใน​ระบบ​ปฏิบัติการ​และ runtime

Build pipeline ใน CI/CD ที่​ยัง​รอ step เดียว — ทีม​หนึ่ง​แตก unit test ออก​เป็น​หลาย job รัน​ขนาน​กัน​บน CI runner จำนวน​มาก​เพื่อ​ให้ pipeline เร็ว​ขึ้น แต่ step สุดท้าย​อย่าง​การ build Docker image หรือ deploy ยัง​คง​ต้อง​รอ​ให้​ทุก job ก่อนหน้า​เสร็จ​ก่อน และ​ตัว build/deploy เอง​ก็​ยัง​เป็น serial step เดียว ผล​คือ​ต่อ​ให้​เพิ่ม runner ขนาน test มาก​แค่​ไหน เวลา​รวม​ของ pipeline ก็​ยัง​ถูก​จำกัด​ด้วย​เวลา​ของ step ที่​ขนาน​ไม่​ได้​เหล่า​นั้น

  • วัด​ก่อน​ขนาน — ใช้ profiler หา​ให้​เจอ​ว่า​สัดส่วน​เวลา​จริง ๆ ที่​ใช้​ไป​กับ​ส่วน​ที่​ทำ​ขนาน​ได้​คือ​เท่า​ไหร่ ก่อน​ตัดสิน​ใจ​ลงทุน​เวลา​เขียน concurrent code หรือ​ซื้อ hardware เพิ่ม
  • โฟกัส​ที่​คอ​ขวด​จริง ไม่ใช่​ส่วน​ที่ optimize ง่าย — การ​เร่ง​ความเร็ว​ส่วน​ที่​ใช้​เวลา​น้อย​อยู่​แล้ว​ให้​ผล​ตอบแทน​ต่ำ​เสมอ ต่อ​ให้ optimize ได้​เก่ง​แค่​ไหน​ก็ตาม ควร​เทียบ​สัดส่วน P ก่อน​เสมอ​ว่า​คุ้ม​ค่า​กับ​ความ​ซับซ้อน​ที่​เพิ่ม​ขึ้น​หรือ​ไม่
  • แยก​ให้​ออก​ระหว่าง “งาน​คงที่” กับ “งาน​ที่​ขยาย​ได้” — ถ้า​ปัญหา​ที่​แก้​อยู่​คือ​ทำงาน​เดิม​ให้​เร็ว​ขึ้น ให้​คิด​แบบ Amdahl’s Law แต่​ถ้า​ปัญหา​คือ​รองรับ workload ที่​ใหญ่​ขึ้น​เรื่อย ๆ ใน​เวลา​เท่า​เดิม ให้​คิด​แบบ Gustafson’s Law ทั้ง​สอง​มุมมอง​นำ​ไป​สู่​กลยุทธ์ scale ที่​ต่าง​กัน
  • ใช้ได้​กับ​ทีม​และ​กระบวนการ ไม่ใช่​แค่ CPU — ถ้า​การ​ตัดสิน​ใจ​ทาง​สถาปัตยกรรม​ทั้งหมด​ต้อง​ผ่าน​คน​คน​เดียว​หรือ​คณะ​กรรมการ​กลุ่ม​เดียว การ​จ้าง​วิศวกร​เพิ่ม​ก็​ไม่​ช่วย​เร่ง throughput ของ​การ​ตัดสิน​ใจ เพราะ “คน​อนุมัติ” กลาย​เป็น​ส่วน serial ของ​กระบวนการ​ทั้งหมด
  • เพดาน​ทาง​ทฤษฎี​ต่ำ​กว่า​ที่​คิด​เสมอ​ใน​ระบบ​จริง — เผื่อ​ใจ​ไว้​ว่า​ปัจจัย​อื่น​อย่าง lock contention, network latency, memory bandwidth จะ​ทำให้ speedup จริง​ต่ำ​กว่า​ตัวเลข​ที่​สูตร​คำนวณ​ได้
// คำนวณ speedup ตาม Amdahl's Law
// parallelFraction คือสัดส่วนงานที่ทำขนานได้ (0.0 - 1.0)
// processorCount คือจำนวน processor หรือ worker ที่ใช้
static double AmdahlSpeedup(double parallelFraction, int processorCount)
{
double serialFraction = 1.0 - parallelFraction;
return 1.0 / (serialFraction + (parallelFraction / processorCount));
}
// ตัวอย่าง: งานที่ขนานได้ 90% เมื่อเพิ่ม processor ไปเรื่อย ๆ
// AmdahlSpeedup(0.9, 2) ≈ 1.82
// AmdahlSpeedup(0.9, 8) ≈ 4.71
// AmdahlSpeedup(0.9, 1000) ≈ 9.91 -- เข้าใกล้เพดาน 1 / (1 - 0.9) = 10 แต่ไม่มีวันถึง