คู่มือเกมแลค › L1 โปรเซสเกมฝั่งไคลเอนต์
GC ฝั่งไคลเอนต์ Client GC (Unity C#, Unreal, Lua)
ID สาเหตุ cg-gc · ผู้รับผิดชอบหลัก พัฒนาไคลเอนต์ (ทีมพัฒนาเกม)
เปิดการ์ดในฉบับหลักที่มีภาพและการทดลอง →
ทั้งเกมหยุดระหว่างเก็บคืนหน่วยความจำที่ใช้แล้วทิ้ง (garbage) จุดสังเกตคือกระตุกเป็นจังหวะสม่ำเสมอ
ทำไม สร้าง string, array และ list ชั่วคราวแล้วทิ้งทุกเฟรม → ผลคือ เมื่อ garbage สะสมมากพอ GC จะหยุดเมนเธรดเพื่อเก็บคืน → บนหน้าจอ กระตุกเป็นจังหวะทุกไม่กี่วินาทีถึงหลายสิบวินาที
- อาการ
- กระตุก, ค้าง
- ปัจจัย
- การหยุดชะงัก
- ใครเจอ
- เราคนเดียว
- เกิดเมื่อไร
- เป็นรอบสม่ำเสมอ, ตอนคนแห่มารวมกัน
- ผู้รับผิดชอบ
- ผู้รับผิดชอบหลัก พัฒนาไคลเอนต์ (ทีมพัฒนาเกม)
- งานฝั่งทีมพัฒนาเกม
- ลดการจัดสรรหน่วยความจำ (เลี่ยงการต่อ string, LINQ และ lambda capture), ใช้ object pool, เปิด incremental GC ไว้ (เป็นค่าเริ่มต้นตั้งแต่ Unity 2020), สั่ง GC ล่วงหน้าในจังหวะที่หยุดได้ เช่น หน้าโหลด
- ตัวเลขที่ควรรู้
- ปกติครั้งละหลักหน่วย ms ถึง 100 ms ส่วนมือถือสเปกต่ำหรือเกมที่ใช้หน่วยความจำมากอาจนานกว่านั้น (ในการทดลองราว 150–170 ms) GC ของ Unity ตรวจทั้ง heap ทุกครั้งที่ทำงาน ยิ่งเกมใช้หน่วยความจำมากก็ยิ่งนาน
- บนกราฟ
- พุ่งเป็นรอบ · เฟรมไทม์, เวลาที่ GC ทำงาน
- จุดที่ต้องดู
- ดู marker GC.Collect และ GC.Alloc ใน Unity Profiler บน development build ส่วน Unreal ดู stat GC และ stat Hitches (บันทึกเฟรมที่เกินเวลาที่ตั้งไว้ใน t.HitchFrameTimeThreshold ลง log)
- สัญญาณว่าใช่
- ทุกเฟรมที่พุ่งมีช่วง GC.Collect ยาวใกล้เคียงกับช่วงที่พุ่ง และเกิดเป็นรอบสม่ำเสมอทุกไม่กี่วินาทีถึงหลายสิบวินาที ในที่ที่คนเยอะ GC.Alloc ต่อเฟรมเพิ่มขึ้น
- สัญญาณว่าไม่ใช่
- เฟรมที่พุ่งไม่มีช่วง GC: น่าจะเป็น “โหลดแบบ synchronous และคอมไพล์ shader บนเมนเธรด” หรือ “ภาระการเรนเดอร์ตัวละครจำนวนมาก”
- วิธีตรวจ
- ต้องมี log หรือเมตริกจากเซิร์ฟเวอร์/ไคลเอนต์เกม
- รายละเอียดเพิ่มเติม
- พบบ่อยในไคลเอนต์ที่เขียนด้วย C# อย่าง Unity ตัวการหลักคือโค้ดที่สร้าง string ของ combat log, ตัวเลขดาเมจ และข้อความ UI ขึ้นใหม่ทุกเฟรม ถ้ากระตุกเฉพาะที่ที่คนเยอะ แปลว่ามีโค้ดที่สร้าง garbage เพิ่มตามจำนวนผู้เล่น incremental GC แบ่งเก็บทีละนิดในแต่ละเฟรม (ค่าเริ่มต้นของ Unity คือ 3 ms) แต่ถ้าสร้าง garbage เร็วกว่าที่เก็บทัน สุดท้ายก็ต้องหยุดเก็บรวดเดียวอยู่ดี Unreal Engine ก็มี GC ของตัวเองที่เก็บกวาด game object ที่ไม่ได้ใช้ ค่าจะต่างกันไปตามเวอร์ชันเอนจินและการตั้งค่า แต่ค่าเริ่มต้นจะทำงานราวทุก 1 นาที จึงอาจเกิดอาการหยุดแวบทุก 1 นาทีได้ ไคลเอนต์ที่เขียนกฎของเกมด้วยสคริปต์อย่าง Lua ยังมี GC ของสคริปต์นั้นทำงานแยกอีกชุดหนึ่ง
แหล่งอ้างอิง
- Garbage collection modes Unity
ค่าเริ่มต้นคือ incremental GC ที่แบ่งเก็บในหลายเฟรม ถ้าปิดไว้ เมนเธรดจะหยุดระหว่างตรวจทั้ง heap และอาจนานถึงหลายร้อย ms - Scripting.GarbageCollector.CollectIncremental Unity
เป้าหมายเริ่มต้นของเวลาที่ incremental GC ใช้ต่อครั้ง (time slice) คือ 3 ms (incrementalTimeSliceNanoseconds) - Garbage Collection Settings in the Unreal Engine Project Settings Epic Games
การตั้งค่าที่ทำให้ GC ของ Unreal ทำงานตามช่วงเวลาที่กำหนด (Time Between Purging Pending Kill Objects หน่วยเป็นวินาที) เอกสารนี้ไม่ได้ระบุตัวเลขค่าเริ่มต้น - Profiler markers reference Unity
GC.Collect: ช่วงที่โค้ดของโปรแกรมหยุดระหว่าง garbage collection (ไม่ถึง 1 ms ไปจนถึงหลายร้อย ms), GC.Alloc: การจัดสรร managed heap - Stat Commands in Unreal Engine Epic Games
stat GC (สถิติ garbage collection), stat Hitches (บันทึกเฟรมที่เกิน t.HitchFrameTimeThreshold ลง log)
สาเหตุที่ควรดูประกอบ
ชั้นเดียวกัน: L1 โปรเซสเกมฝั่งไคลเอนต์
สาเหตุจากชั้นอื่นที่ทำให้เกิดอาการเดียวกัน (กระตุก)
ดูการ์ดในฉบับหลักที่มีภาพและการทดลอง