การทำโปรไฟล์เกม WebGL ด้วย Flag "เกี่ยวกับ:การติดตาม"

Lilli Thompson
Lilli Thompson

หากวัดไม่ได้ ก็ปรับปรุงไม่ได้

Lord Kelvin

หากต้องการให้เกม HTML5 ทำงานเร็วขึ้น คุณต้องระบุจุดคอขวดด้านประสิทธิภาพก่อน ซึ่งอาจทำได้ยาก การประเมินข้อมูลเฟรมต่อวินาที (FPS) เป็นเพียงจุดเริ่มต้นเท่านั้น แต่หากต้องการเห็นภาพรวมทั้งหมด คุณต้องเข้าใจความแตกต่างของกิจกรรมใน Chrome

เครื่องมือ about:tracing ให้ข้อมูลเชิงลึกที่ช่วยให้คุณหลีกเลี่ยงการแก้ปัญหาชั่วคราวที่มุ่งเน้นการปรับปรุงประสิทธิภาพ แต่จริงๆ แล้วเป็นการคาดเดาที่มีเจตนาดี คุณจะช่วยประหยัดเวลาและแรงได้มาก รวมถึงเห็นภาพชัดเจนขึ้นว่า Chrome กำลังทำอะไรกับแต่ละเฟรม และนำข้อมูลนี้ไปใช้เพิ่มประสิทธิภาพเกม

สวัสดี about:tracing

เครื่องมือ about:tracing ของ Chrome ช่วยให้คุณเห็นภาพรวมของกิจกรรมทั้งหมดของ Chrome ในช่วงเวลาหนึ่งๆ โดยละเอียดมากจนคุณอาจรู้สึกสับสนในตอนแรก ฟังก์ชันหลายอย่างใน Chrome มีเครื่องมือสําหรับการติดตามอยู่แล้วตั้งแต่แกะกล่อง คุณจึงยังใช้ about:tracing เพื่อติดตามประสิทธิภาพได้โดยไม่ต้องทำเครื่องมือวัดด้วยตนเอง (ดูส่วนถัดไปเกี่ยวกับการติดแท็ก JS ด้วยตนเอง)

หากต้องการดูมุมมองการติดตาม ให้พิมพ์ "about:tracing" ลงในแถบอเนกประสงค์ (แถบที่อยู่) ของ Chrome

แถบอเนกประสงค์ของ Chrome
พิมพ์ "about:tracing" ในแถบอเนกประสงค์ของ Chrome

จากเครื่องมือการติดตาม คุณสามารถเริ่มบันทึก เรียกใช้เกมเป็นเวลา 2-3 วินาที แล้วดูข้อมูลการติดตามได้ ต่อไปนี้คือตัวอย่างลักษณะที่ข้อมูลอาจปรากฏ

ผลการติดตามแบบง่าย
ผลการติดตามแบบง่าย

ใช่ เป็นเรื่องที่สับสนจริงๆ มาพูดถึงวิธีอ่านกัน

แต่ละแถวแสดงถึงโปรไฟล์ของกระบวนการ แกนซ้าย-ขวาแสดงเวลา และกล่องสีแต่ละช่องคือการเรียกใช้ฟังก์ชันที่มีเครื่องมือวัด แถวต่างๆ แสดงทรัพยากรหลายประเภท รายการที่น่าสนใจที่สุดสำหรับการจัดโปรไฟล์เกมคือ CrGpuMain ซึ่งแสดงสิ่งที่หน่วยประมวลผลกราฟิก (GPU) กำลังทํา และ CrRendererMain การติดตามแต่ละรายการจะมีบรรทัด CrRendererMain สําหรับแท็บที่เปิดอยู่แต่ละแท็บในช่วงการติดตาม (รวมถึงแท็บ about:tracing เอง)

เมื่ออ่านข้อมูลการติดตาม งานแรกของคุณคือต้องระบุแถว CrRendererMain ที่สอดคล้องกับเกมของคุณ

ผลการติดตามแบบง่ายที่ไฮไลต์
ผลการติดตามแบบง่ายที่ไฮไลต์

ในตัวอย่างนี้ 2 รายการที่ตรงกันคือ 2216 และ 6516 ขออภัย ปัจจุบันยังไม่มีวิธีเลือกแอปพลิเคชันอย่างละเอียด ยกเว้นการค้นหาบรรทัดที่ทำการอัปเดตเป็นระยะๆ จำนวนมาก (หรือหากคุณได้ติดตั้งเครื่องมือวัดโค้ดด้วยจุดติดตามด้วยตนเอง ให้มองหาบรรทัดที่เก็บข้อมูลการติดตาม) ในตัวอย่างนี้ ดูเหมือนว่า 6516 จะเรียกใช้ลูปหลักจากความถี่ในการอัปเดต หากคุณปิดแท็บอื่นๆ ทั้งหมดก่อนเริ่มการติดตาม การค้นหา CrRendererMain ที่ถูกต้องจะง่ายขึ้น แต่อาจยังมีแถว CrRendererMain สำหรับกระบวนการอื่นๆ นอกเหนือจากเกมของคุณ

การค้นหาเฟรม

เมื่อพบแถวที่ถูกต้องในเครื่องมือการติดตามสำหรับเกมแล้ว ขั้นตอนถัดไปคือการค้นหาลูปหลัก ลูปหลักมีลักษณะเป็นรูปแบบที่ซ้ำกันในข้อมูลการติดตาม คุณสามารถไปยังส่วนต่างๆ ของข้อมูลการติดตามได้โดยใช้แป้น W, A, S, D โดยแป้น A และ D ใช้เพื่อเลื่อนไปทางซ้ายหรือขวา (ย้อนกลับและไปข้างหน้าตามลำดับเวลา) และแป้น W และ S ใช้เพื่อซูมเข้าและออก คุณควรคาดหวังว่าลูปหลักจะเป็นรูปแบบที่ซ้ำกันทุก 16 มิลลิวินาทีหากเกมทำงานที่ 60Hz

ดูเหมือนว่าจะเป็นเฟรมการดําเนินการ 3 เฟรม
ดูเหมือนเฟรมการดําเนินการ 3 เฟรม

เมื่อพบจุดที่หัวใจเต้นของเกมแล้ว คุณสามารถเจาะลึกสิ่งที่โค้ดทําในแต่ละเฟรมได้ ใช้แป้น W, A, S, D เพื่อซูมเข้าจนกว่าคุณจะอ่านข้อความในช่องฟังก์ชันได้

เจาะลึกเฟรมการดําเนินการ
เจาะลึกเฟรมการดําเนินการ

คอลเล็กชันกล่องนี้แสดงชุดการเรียกฟังก์ชัน โดยแต่ละการเรียกจะแสดงด้วยกล่องสี แต่ละฟังก์ชันเรียกโดยกล่องด้านบน ดังนั้นในกรณีนี้ คุณจะเห็นได้ว่า MessageLoop::RunTask เรียก RenderWidget::OnSwapBuffersComplete ซึ่งเรียก RenderWidget::DoDeferredUpdate และอื่นๆ ต่อๆ ไป การอ่านข้อมูลนี้จะช่วยให้คุณเห็นภาพรวมทั้งหมดของสิ่งที่เรียกใช้อะไรและระยะเวลาที่ใช้ในการดำเนินการแต่ละครั้ง

แต่ปัญหาอยู่ตรงที่ว่า ข้อมูลที่ about:tracing แสดงคือฟังก์ชันการเรียกแบบดิบจากซอร์สโค้ดของ Chrome คุณอาจเดาได้ว่าฟังก์ชันแต่ละรายการทําอะไรจากชื่อ แต่ข้อมูลนั้นไม่ค่อยเป็นมิตรกับผู้ใช้ การดูภาพรวมของเฟรมมีประโยชน์ แต่คุณก็ต้องใช้ภาพที่สามารถอ่านได้ง่ายกว่านี้เพื่อดูว่าเกิดอะไรขึ้น

การเพิ่มแท็กการติดตาม

แต่โชคดีที่เรามีวิธีง่ายๆ ในการเพิ่มเครื่องมือวัดผลด้วยตนเองลงในโค้ดเพื่อสร้างข้อมูลการติดตาม ซึ่งก็คือ console.time และ console.timeEnd

console.time("update");
update();
console.timeEnd("update");
console.time("render");
update();
console.timeEnd("render");

โค้ดด้านบนจะสร้างช่องใหม่ในชื่อมุมมองการติดตามพร้อมแท็กที่ระบุ ดังนั้นหากคุณเรียกใช้แอปอีกครั้ง คุณจะเห็นช่อง "อัปเดต" และ "แสดงผล" ที่แสดงเวลาผ่านไประหว่างการเรียกใช้เริ่มต้นและสิ้นสุดสําหรับแต่ละแท็ก

แท็กที่เพิ่มด้วยตนเอง
แท็กที่เพิ่มด้วยตนเอง

ซึ่งจะช่วยให้คุณสร้างข้อมูลการติดตามที่มนุษย์อ่านได้เพื่อติดตามจุดร้อนในโค้ดได้

GPU หรือ CPU

เมื่อใช้กราฟิกที่เร่งด้วยฮาร์ดแวร์ คำถามที่สําคัญที่สุดอย่างหนึ่งที่คุณถามได้ในระหว่างการโปรไฟล์คือ โค้ดนี้ใช้ GPU หรือ CPU ในแต่ละเฟรม คุณจะทำการเรนเดอร์บางอย่างใน GPU และตรรกะบางอย่างใน CPU หากต้องการทำความเข้าใจว่าอะไรทำให้เกมช้า คุณจะต้องดูว่างานมีความสมดุลกันอย่างไรระหว่างทรัพยากร 2 รายการนี้

ก่อนอื่น ให้ค้นหาบรรทัดในมุมมองการติดตามชื่อ CrGPUMain ซึ่งระบุว่า GPU ไม่ว่างในช่วงเวลาหนึ่งๆ หรือไม่

ร่องรอย GPU และ CPU

คุณจะเห็นได้ว่าเฟรมทุกเฟรมของเกมทําให้ CPU ทํางานใน CrRendererMain และ GPU การติดตามด้านบนแสดง Use Case ที่ง่ายมากซึ่งทั้ง CPU และ GPU ไม่ได้ใช้งานเกือบตลอดเฟรม 16 มิลลิวินาทีแต่ละเฟรม

มุมมองการติดตามจะมีประโยชน์อย่างยิ่งเมื่อเกมทำงานช้าและคุณไม่แน่ใจว่าทรัพยากรใดใช้เกินขีดจำกัด การดูความเชื่อมโยงของบรรทัด GPU และ CPU คือกุญแจสำคัญในการแก้ไขข้อบกพร่อง ใช้ตัวอย่างเดิม แต่เพิ่มการทำงานอีกเล็กน้อยในลูปการอัปเดต

console.time("update");
doExtraWork();
update(Math.min(50, now - time));
console.timeEnd("update");

console.time("render");
render();
console.timeEnd("render");

ตอนนี้คุณจะเห็นการติดตามที่มีลักษณะดังนี้

ร่องรอย GPU และ CPU

การติดตามนี้บอกอะไรเราบ้าง เราเห็นว่าเฟรมในภาพใช้เวลาประมาณ 2270 มิลลิวินาทีถึง 2320 มิลลิวินาที ซึ่งหมายความว่าแต่ละเฟรมใช้เวลาประมาณ 50 มิลลิวินาที (อัตราเฟรม 20 Hz) คุณจะเห็นกล่องสีเล็กๆ ที่แสดงถึงฟังก์ชันการแสดงผลข้างช่องอัปเดต แต่เฟรมนั้นเต็มไปด้วยการอัปเดต

คุณจะเห็นว่า GPU ยังคงทำงานอยู่เฉยๆ เกือบทุกเฟรม ซึ่งตรงข้ามกับสิ่งที่เกิดขึ้นใน CPU หากต้องการเพิ่มประสิทธิภาพโค้ดนี้ ให้มองหาการดำเนินการที่ทำได้โดยใช้โค้ด Shader แล้วย้ายไปยัง GPU เพื่อใช้ทรัพยากรอย่างคุ้มค่าที่สุด

จะเกิดอะไรขึ้นเมื่อโค้ด Shader ทำงานช้าและ GPU ทำงานหนักเกินไป จะเกิดอะไรขึ้นหากเรานำงานที่ไม่จำเป็นออกจาก CPU และเพิ่มงานบางอย่างในโค้ด Fragment Shader แทน นี่คือตัวอย่าง Shader ระดับเศษส่วนที่สิ้นเปลืองทรัพยากรโดยไม่จำเป็น

#ifdef GL_ES
precision highp float;
#endif
void main(void) {
  for(int i=0; i<9999; i++) {
    gl_FragColor = vec4(1.0, 0, 0, 1.0);
  }
}

ร่องรอยของโค้ดที่ใช้โปรแกรมเปลี่ยนสีนั้นมีลักษณะเป็นอย่างไร

ร่องรอย GPU และ CPU เมื่อใช้โค้ด GPU ที่ช้า
การติดตาม GPU และ CPU เมื่อใช้โค้ด GPU ที่ช้า

โปรดจดบันทึกระยะเวลาของเฟรมอีกครั้ง รูปแบบที่ซ้ำกันนี้อยู่ในช่วงประมาณ 2750-2950 มิลลิวินาที โดยมีระยะเวลา 200 มิลลิวินาที (อัตราเฟรมประมาณ 5 Hz) บรรทัด CrRendererMain ว่างเปล่าเกือบทั้งหมด ซึ่งหมายความว่า CPU ไม่ได้ทำงานเกือบตลอดเวลา ขณะที่ GPU ทำงานหนักเกินไป นี่เป็นสัญญาณที่ชัดเจนว่าชิเดอร์ของคุณมีน้ำหนักมากเกินไป

หากไม่ทราบสาเหตุที่ทำให้เกิดอัตราเฟรมต่ำ คุณอาจเห็นการอัปเดต 5 Hz และอาจอยากเข้าไปแก้ไขโค้ดเกมและเริ่มพยายามเพิ่มประสิทธิภาพหรือนำตรรกะเกมออก ในกรณีนี้ การดำเนินการดังกล่าวจะไร้ประโยชน์อย่างยิ่ง เนื่องจากตรรกะในลูปเกมไม่ใช่สิ่งที่ทำให้เสียเวลา อันที่จริง สิ่งที่การติดตามนี้บ่งชี้คือการทำงานเพิ่มเติมของ CPU ในแต่ละเฟรมจะ "ว่าง" อยู่เนื่องจาก CPU ทำงานอยู่เฉยๆ ดังนั้นการให้งานเพิ่มเติมจะไม่ส่งผลต่อระยะเวลาของเฟรม

ตัวอย่างจริง

มาดูกันว่าข้อมูลการติดตามจากเกมจริงมีลักษณะเป็นอย่างไร สิ่งที่น่าสนใจอย่างหนึ่งเกี่ยวกับเกมที่สร้างด้วยเทคโนโลยีเว็บแบบเปิดคือคุณสามารถดูสิ่งที่เกิดขึ้นในผลิตภัณฑ์โปรดของคุณ หากต้องการทดสอบเครื่องมือโปรไฟล์ คุณสามารถเลือกเกม WebGL ที่ชอบจาก Chrome เว็บสโตร์และโปรไฟล์ด้วย about:tracing นี่คือตัวอย่างการติดตามที่มาจากเกม Skid Racer บน WebGL ที่ยอดเยี่ยม

การติดตามเกมจริง
การติดตามเกมจริง

ดูเหมือนว่าแต่ละเฟรมจะใช้เวลาประมาณ 20 มิลลิวินาที ซึ่งหมายความว่าอัตราเฟรมจะอยู่ที่ประมาณ 50 FPS คุณจะเห็นได้ว่างานมีความสมดุลระหว่าง CPU และ GPU แต่ GPU เป็นทรัพยากรที่มีความต้องการมากที่สุด หากต้องการดูตัวอย่างจริงของเกม WebGL ให้ลองเล่นกับเกมบางเกมใน Chrome เว็บสโตร์ที่สร้างขึ้นด้วย WebGL ซึ่งรวมถึงเกมต่อไปนี้

บทสรุป

หากต้องการให้เกมทำงานที่ 60Hz การดำเนินการทั้งหมดสำหรับเฟรมแต่ละเฟรมต้องใช้เวลาไม่เกิน 16 มิลลิวินาทีของ CPU และ 16 มิลลิวินาทีของ GPU คุณมีทรัพยากร 2 รายการที่ใช้ได้พร้อมกัน และสามารถสลับงานระหว่างทรัพยากรเพื่อเพิ่มประสิทธิภาพสูงสุด มุมมอง about:tracing ของ Chrome เป็นเครื่องมือที่มีประโยชน์อย่างยิ่งในการรับข้อมูลเชิงลึกเกี่ยวกับสิ่งที่โค้ดของคุณทําอยู่จริง และจะช่วยให้คุณใช้เวลาในการพัฒนาได้คุ้มค่าที่สุดด้วยการแก้ปัญหาที่เหมาะสม

ขั้นตอนถัดไปคือ

นอกจาก GPU แล้ว คุณยังติดตามส่วนอื่นๆ ของรันไทม์ Chrome ได้ด้วย Chrome Canary ซึ่งเป็นเวอร์ชันเริ่มต้นของ Chrome มีเครื่องมือในการติดตาม IO, IndexedDB และกิจกรรมอื่นๆ อีกหลายอย่าง คุณควรอ่านบทความ Chromium นี้เพื่อให้เข้าใจสถานะปัจจุบันของเหตุการณ์การติดตามได้ดีขึ้น

หากคุณเป็นนักพัฒนาเกมบนเว็บ โปรดดูวิดีโอด้านล่าง นี่เป็นงานนำเสนอจากทีมผู้สนับสนุนนักพัฒนาเกมของ Google ที่ GDC 2012 เกี่ยวกับการเพิ่มประสิทธิภาพเกมใน Chrome