Wall of Text #11: C Array and Fraps

น้องเค้ามีคำถามจากการบ้านว่า

จงเปรียบเทียบระหว่าง Array ในภาษาเก่าๆ กับ Python list

ผมชอบคำถามนี้นะ คำตอบที่เค้าเขียนคือ

ในภาษา C สมาชิกของ Array ทั้งหมดจะต้องเป็น data type เดียวกันทั้งหมด และขนาดของ Array จะตายตัว แต่ใน Python list สามารถคละ data type กันได้ และสามารถเปลี่ยนขนาดของ Array ได้โดยการเพิ่มหรือลบสมาชิก

ที่น่าสนใจคือ จริงๆ แล้ว C Array ไม่ได้มีขนาดตายตัว…. มันไม่มีขนาดด้วยซ้ำ

Array ในภาษา C จริงๆ แล้วมันคือ Pointer ซึ่งเก็บที่อยู่ของข้อมูล การใช้ Pointer ในภาษา C จะประมาณนี้

ถ้าลองรันดูจะเห็นว่า output คือ 10 ถึงเราจะไม่ได้แก้ x ตรงๆ แต่เราแก้ผ่าน pointer แทน

คำสั่ง *x_ptr จะเป็นการอ่านค่าที่ตำแหน่งที่ x_ptr ชี้ไปอยู่ ซึ่งจริงๆ แล้วคำสั่งนี้มีค่าเท่ากับ x_ptr[0]… แล้ว x_ptr[1] คืออะไรล่ะ?

x_ptr[1] มีค่าเท่ากับ *(x_ptr + 1) เนื่องจากใน C สมาชิกทุกตัวใน array จะอยู่ใน memory ตำแหน่งที่ติดกันไปเรื่อยๆ และสมาชิกทุกตัวมีขนาดเท่ากัน (เพราะเป็น data type เดียวกัน) ดังนั้นถ้าเราทราบที่อยู่ของสมาชิกตัวแรก ตัวถัดไปก็คือเลื่อนไปจากสมาชิกตัวแรกเท่ากับขนาดของสมาชิก 1 ตัว

ดังนั้นตัวแปรที่เก็บ C array จริงๆ แล้วไม่ได้เก็บ array แต่เก็บตำแหน่งของสมาชิกตัวแรกเท่านั้น ส่วนการประกาศ array จริงๆ แล้วก็คือการจองพื้นที่พอให้เก็บสมาชิกใน array ตามจำนวนที่ขอ และคืนตำแหน่งของสมาชิกตัวแรกมาให้

แต่จริงๆ แล้ว เราสามารถอ่านหรือเขียนค่าไปเกินกว่าพื้นที่ที่ขอได้เช่นเดียวกัน เนื่องจาก C ไม่มีการเก็บไว้ด้วยซ้ำว่า array มีขนาดเท่าไร (แต่ไม่การันตีผลลัพท์นะว่าถ้าอ่านเขียนเกินพื้นที่ที่จองไว้จะได้ผลอย่างไร)

Portable assembly

หลังฉากของภาษา C แล้วน่าสนใจว่ามันเป็นภาษาที่ไม่ค่อยสูงกว่า assembly เท่าไรนัก มันไม่พยายามซ่อนการทำงานของคอมพิวเตอร์ ซึ่งก็ตรงตามสิ่งที่ผู้สร้าง C คิดไว้คือมันเป็นเพียงแค่ Portable Assembly

ซึ่งในภาษา C เองก็ยังมีคำสั่ง __asm__ ที่เอาไว้เขียน assembly code ผสมกับภาษา C ได้เมื่อจำเป็น

และคำสั่งนี้ก็มีใช้อยู่ในโค้ดจริงๆ ด้วย เช่นถ้าเราไปอ่านโค้ดของ H.264 decoder ก็จะเห็นว่าเค้าใช้ Assembly ผสมกับโค้ด C เพราะการ decode video นั้นอาจจะใช้ฟีเจอร์เฉพาะของ CPU เข้ามาช่วยได้ ซึ่ง compiler อาจจะไม่สามารถแปลงโค้ด C เป็นคำสั่ง CPU พวกนี้ได้ (*)

Hardware acceleration

หลายๆ อย่างที่คอมพิวเตอร์เราทำอยู่นั้น ถึงแม้ว่าคอมพิวเตอร์จะเร็วขึ้นแต่การที่ไม่มี hardware พิเศษมารองรับก็อาจจะทำให้ทำงานได้ช้าหรือทำงานไม่ได้เลย

GPU ของ Raspberry Pi นั้นสามารถถอดรหัส video H.264 (mp4) ได้สบายๆ หลายคนเอา Raspberry Pi มาลง Kodi ดูหนังซึ่งก็จะเห็นว่าภาพก็ลื่นดี แต่ถ้าโปรแกรมที่ใช้นั้นไม่รองรับการใช้ hardware acceleration แล้วก็จะพบว่า CPU ของ Raspberry Pi decode ไฟล์ mp4 ไม่ไหวเลย

บน PC ก็เช่นเดียวกัน ถ้าเราดูไฟล์วิดีโอ MP4 ในเครื่อง จะเห็นว่า CPU load จะไม่ถึง 10% เท่านั้น แต่ถ้าลองดู YouTube 4K จะพบว่า CPU ขึ้นไปสูงถึง 50-100% เลยทีเดียว เนื่องจากฟอร์แมต VP9 (ในขณะที่เขียนนี้) hardware หลายๆ รุ่นยังไม่รองรับการถอดรหัส VP9 เลยจะต้องใช้พลัง CPU ประมวลผล ซึ่งนอกจากจะหน่วงเครื่องแล้วยังเปลืองแบตเตอรี่อีกด้วย

อย่าลืมปรับเป็น 4k 60fps

สมัย Windows XP ถ้าใครเคย Capture โปรแกรมวิดีโอด้วยปุ่ม print screen ก็จะเห็นว่ากรอบที่เล่น video จะเป็นจอดำ หรือเวลากด Print screen บนเกม Full screen ก็เช่นกัน วิธีแก้ไขก็คือใช้โปรแกรมพิเศษที่จับภาพหน้าจอเกมได้ เช่น Fraps

ที่เป็นอย่างนี้ก็เพราะ Video หรือเกมก็แล้วแต่นั้น ภาพถูกสร้างขึ้นบนการ์ดจอโดยตรงโดยไม่ได้ผ่าน desktop เมื่อเรากด print screen จึงจะเห็นแค่ฉากหลังดำๆ เท่านั้น

ตั้งแต่ Windows Vista เป็นต้นมา Desktop compositing (Aero) เป็นที่นิยมมากขึ้นซึ่งทำให้ประสบการณ์ใช้งาน desktop ดีขึ้น ภาพไม่กระพริบ เวลาลากโปรแกรมไม่กระตุก (ข้อเสียคือเกมที่รันใน Windowed mode จะไม่สามารถรันเร็วกว่าเฟรมเรทของ Compositor ได้ ซึ่งถ้าใครใช้จอ 120Hz อาจจะต้องรันเกมใน Full screen แทน) ซึ่ง Desktop compositing นี้ทำงานบนการ์ดจอทำให้การทำ Print screen เปลี่ยนไปและเรามักจะสามารถ screenshot ทุกอย่างบนหน้าจอได้หมดแล้ว นอกเหนือจากเกม Full screen


ผมคิดว่าการศึกษาการทำงานของคอมพิวเตอร์เป็นเรื่องที่น่าสนใจมาก ถ้ามีคนถามว่าทำไม print screen แล้วจอดำ เราจะได้เข้าใจเหตุผลว่ามันไม่ใช่ “เพราะคุณกด print screen จอเกมไม่ได้ถ้าไม่ได้ใช้ Fraps” แต่เพราะภาพมันอยู่ที่การ์ดจอแล้วเรา print screen บน CPU ต่างหากล่ะ