BLOG

อะไรคือ Core อะไรคือ Thread?

มี Core เยอะกว่า Thread ดี หรือมี Thread เยอะกว่า Core ดี

สำหรับ CPU Gen 9 ที่เป็นรุ่น 9700K นั้น มีเสปคในการปล่อยความร้อนเท่าเดิมคือ 95W แต่ว่ามีจำนวน Core เพิ่มขึ้นจาก 6 เป็น 8 Core เพิ่มขึ้น 33% แต่ว่าถูกตัดความสามารถ Simultaneous Multi Threading (SMT) ออกไป หรือที่ Intel เรียกมันว่า Hyper Threading ทำให้จำนวน Hardware Thread เหลือแค่ 8 จากเดิมที่มี 12 - หรือลดลง 33% อีกเหมือนกัน!

ตกลง Core / Thread นี่ มันคืออะไรของมันกันแน่นะ แล้วมี Core เยอะกว่า Thread ดี หรือมี Thread เยอะกว่า Core ดี วันนี้ขอมาเขียนให้อ่านกันซะหน่อย ยาวนะ เตรียมกาแฟไว้ด้วย

อย่างแรกเลย อะไรคือ CPU 1 Core?

พอมาเริ่มหัวข้อนี้มาปุ๊บ อย่างแรกเลยที่ผมต้องทำก็คือ ต้องไปเปิด Backup เอาไฟล์สมัยเรียนที่อาจารย์สอนมาทบทวนความรู้กันเลยทีเดียว (ซึ่งผมก็เซฟใส่พื้นที่บน Microsoft Azure ไว้ เหมือนกับที่ผมเอามาทำเป็นระบบ Cloud Drive ให้ลูกค้าที่มีเครื่อง LEVEL51 ใช้คนละ 1TB นั่นแหละ :D) วิชาที่พูดถึงเรื่องนี้ คือวิชาชื่อ Computer Architecture นะ ถ้าอ่านแล้วสนใจ หรือ อินกับเรื่องแบบนี้ วิชานี้อยู่ในหลักสูตร วิทยาศาสตร์คอมพิวเตอร์ (Computer Science) ซึ่งผมเองก็เรียนรู้หลักสูตรนี้มาจาก มหาวิทยาลัยอัสสัมชัญครับ (ABAC / AU) ครับ ผมรู้สึกยินดีที่ได้เขียนโพสนี้มากเลยนะ เพราะว่าได้เอาความรู้เก่าๆ มาทบทวน และก็ด้วยวัยที่แก่ขึ้นความสามารถภาษาอังกฤษก็เยอะขึ้นอีก กลับไปอ่านเรื่องเดิมอีกรอบเลยได้ความเข้าใจขึ้นอีกหลายเรื่องเลย และ Wikipedia/Wikichip ก็ช่วยให้การเรียนรู้เรื่องนี้ง่ายขึ้นกว่าเดิมมากๆ เลยด้วย

กลับมาที่ CPU "1 Core" กันก่อนว่ามันหน้าตาเป็นอย่างไร เผื่อท่านที่ไม่ทราบ ต้องบอกก่อนว่า CPU นั้น ไม่ได้หมายถึงกล่องสีดำใหญ่ๆ (เดี๋ยวนี้มีติดไฟกันวิบวับมากด้วย) ที่เราเอาจอภาพ คีย์บอร์ด แล้วก็เมาส์เสียบเข้าไปนะ ขอแยกเรียกสิ่งนั้นว่า "เคส" แล้วกันนะ

ชื่อเต็มของ CPU มันคือ Central Processing Unit หรือ หน่วยประมวลผลกลาง บางคนก็เรียกหน่วยประมวลผลหลัก มันเป็นส่วนหนึ่งที่อยู่ในเคสอีกที ถ้าลองเปิดเคสออกดู เราจะพบสิ่งสำคัญอยู่ตามนี้

  • เมนบอร์ด หรือ แผงวงจรหลัก ที่เป็นฐาน ให้ทุกอย่างที่เกี่ยวกับคอมพิวเตอร์ของเราเสียบลงไปให้มันต่อกัน เปิดเคสมา เราจะเจอชิ้นนี้ชัดเจนสุด (มองข้าม RTX นั่นไปก่อน~!)
  • RAM (หน่วยความจำหลัก) ที่เสียบอยู่กับเมนบอร์ด 
  • CPU ที่เสียบอยู่กับเมนบอร์ดเหมือนกัน แต่ว่าเรามักจะมองไม่เห็นมัน เนื่องจากจะมีชุดระบายความร้อนขนาดใหญ่มาก หรือไม่ก็ปั๊มสูบน้ำ ปิดทับมันอยู่

แล้ว SSD, HDD, การ์ดจอ, ไฟ RGB หลากสีเรา ไม่อยู่ในสิ่งสำคัญนี้เหรอ? ทั้งหมดนั้นไม่มีความเกี่ยวข้องกับ "คอมพิวเตอร์" (Computer = เครื่องคำนวณ) เลยนะ จริงๆ ในมุมมองของ CPU แล้ว อุปกรณ์อื่นๆ ที่ต่อเข้ามา มันถูกมองเป็นหน่วยความจำ (Memory-mapped I/O) เลยละ เพราะว่าอะไร ต้องไปอ่านกันต่อ

Von Neumann Machine

คอมพิวเตอร์ที่เราใช้อยู่ทุกวันนี้ ถูกออกแบบตามหลักสถาปัตยกรรม (Architecture) ที่คุณ John Von Neumann เป็นคนคิดขึ้น โดยแกเขียนความคิดด้วยลายมือ ความยาว 101 หน้ากระดาษ ระหว่างการโดยสารทางรถไฟไปยัง Los Alamos ในไป 1945 ซึ่งในขณะนั้น เรามี "Computer" ย้ำอีกครั้ง ว่า คือ "เครื่องคำนวณ" ใช้กันอยู่แล้วนะ คือ ENIAC แต่ว่าปัญหาของเครื่องคอมพิวเตอร์ (โดยเฉพาะ ENIAC ซึ่งเดาว่าคุณ Von Neumann แกก็เป็นหนึ่งในคนที่ต้องใช้เครื่องนี้ด้วย) ในตอนนั้นก็คือ มันถูกออกแบบมาเฉพาะอย่าง เราไม่สามารถเปลี่ยน "โปรแกรม" ของมันได้โดยง่าย จะเปลี่ยนกันทีคือต้องสับสวิตช์เสียบสายกันเลยทีเดียว ตามที่ Hilight เลย

แต่ Vonn Neumann คือท่านแรก ที่เสนอแนวความคิดของ Stored Program Computer ออกมาเป็นลายลักษณ์อักษร รวมถึงออกแบบมันออกมาเป็นจริงเป็นจังเลยด้วย (ใน Wikipedia กล่าวถึงว่า มีการพูดถึง Stored Program Computer อยู่ก่อนแล้ว แต่คุณ Vonn Neumann คือคนที่สรุปออกมา) ในเอกสารที่ชื่อว่า First Draft of a Report on the EDVAC ด้วยหลักการ Stored Program Computer นี้ ทำให้เราสามารถพัฒนา "ภาษาในการเขียนโปรแกรม" ขึ้นมาภายหลังได้ เนื่องจากว่าคอมพิวเตอร์ที่ออกแบบตามสถาปัตยกรรมนี้ เอื้อให้เราสามารถสร้าง "โปรแกรมที่สร้างโปรแกรม" ขึ้นมาได้ (Compiler - เช่น gcc)

Firstdraftofrepo00vonn 0003.jpg
By John von Neumann - https://archive.org/stream/firstdraftofrepo00vonn#page/n1/mode/2up, Public Domain, Link

(เขียนถึงตรงนี้ ทำให้นึกถึง TED Talk ตอนนึงเลยว่า เราควรปล่อยสมองเราเข้าสู่ 'Default Mode' บ้าง ก็คือให้มีเวลาว่างๆ โล่งๆ ให้มันประมวลผลข้อมูลต่างๆ บ้าง สมัยนี้ถ้านั่งรถไฟนานขนาดนั้น เราก็คงฆ่าเวลาด้วย 'Screen Time' กันสินะ มีเพื่อนผมคนสองคนแล้วที่เลี้ยงลูกโดยไม่ให้ใช้งาน 'หน้าจอ' เลย เดี๋ยวเด็ก 4 คนนี้โตขึ้นผมจะมาอัพเดทให้อีกทีว่าเขาไปทำอะไรอยู่ที่ไหนกันบ้าง :) )

ในทางทฤษฎีแล้ว หน้าตาของ Vonn Neumann Architecture (หรือบางคนเรียกลงท้ายว่า Machine) จะเป็นแบบนี้

Von Neumann Architecture.svg
By Kapooht - Own work, CC BY-SA 3.0, Link

ก็คือ คอมพิวเตอร์ ในแบบของคุณ Vonn Neumann นี้ ประกอบไปด้วย

  • Input / Output Device - ก็คืออุปกรณ์ที่เอาไว้รับคำสั่ง (Input) กับ แสดงผลลัพธ์ (Output)
  • Central Processing Unit - โดยภายในประกอบไปด้วย
    • Control Unit (CU) - เป็นตัวที่ควบคุมการทำงานของ CPU อีกที ว่า เมื่อได้รับ "คำสั่ง" มาแล้ว (ใน First Draft เรียกว่า Order หรือในศัพท์ปัจจุบันเราใช้คำว่า "Instruction" เพื่อให้ไม่กำกวม) จะต้องทำงานอย่างไรบ้าง

      เช่น ผมอาจจะกำหนดไว้ว่า ถ้าได้รับคำสั่งว่า [บวก,1,2,9] หมายถึง เอาข้อมูลในหน่วยความจำช่องที่ 1 บวกกับหน่วยความจำช่องที่ 2 แล้วเอาผลลัพธ์ไปเก็บในหน่วยความจำช่องที่ 9 เป็นต้น แต่ว่าตัว CU จะไม่มีความสามารถในการบวกเลขนะ นั่นเป็นหน้าที่ของ ALU นะ ตัว CU จะไปสั่ง ALU อีกที
    • Arithmetic and Logic Unit (ALU) - เป็นตัวที่ทำหน้าที่คำนวณเกี่ยวกับตัวเลข (Arithmetic - เช่น บวก ลบ คูณ หาร) และคำนวณด้านตรรกะศาสตร์ (ถ้า P เป็นจริง Q เป็นจริงแล้ว P AND Q เป็นจริง…) ตัว ALU จะเป็น Fixed Program ก็คือมันสามารถทำงานได้ เท่าที่ออกแบบมาเท่านั้น อย่างถ้าออกแบบมาให้ บวก ลบ คูณ หาร ได้ มันก็จะทำได้เท่านั้น ถ้าต้องการให้มันหาเลขยกกำลังเพิ่ม ก็ทำไม่ได้ ต้องการให้มันเปรียบเทียบข้อมูลก่อนแล้วทำแบบนี้ (If...Then...) ก็ทำไม่ได้

      แต่ว่าเราก็สามารถกำหนดว่า ถ้าได้รับคำสั่งว่า [ยกกำลัง,2,1,14] หมายถึง ให้เอาข้อมูลในหน่วยความจำช่อง 2 ยกกำลังด้วยข้อมูลจากช่อง 1 เก็บไว้ในช่องที่ 14 และตอนทำงาน เราก็ออกแบบให้ CU ไปสั่งให้ ALU คูณเลขกันหลายๆ ครั้ง เพื่อจำลองการหาเลขยกกำลังได้ หรือถ้าท่านที่รู้จักกับการเขียนโปรแกรม ตัว CU นี้ ก็เหมือนเป็น "Interpreter" นั่นเอง
  • Memory Unit - หน่วยความจำหลัก ที่ใช้ในการเก็บข้อมูลและคำสั่ง ที่สั่งให้ CPU ทำงาน ซึ่งความแตกต่างของ Stored Program Computer กับ Computer แบบเดิม ก็อยู่ตรงที่ในหน่วยความจำหลักของมัน สามารถมีได้ทั้ง ข้อมูล และก็ คำสั่งนี้เอง

อ่านถึงตรงนี้แล้ว เราก็เลยพอจะสรุปได้ว่าการที่จะเรียกว่าเป็น CPU 1 Core ได้นั้น ก็คือมันจะต้องมี CU 1 ตัว ที่เป็นคนสั่งงาน ALU อีก 1 ตัว ที่อยู่ด้วยกันนี่เอง และคอมพิวเตอร์ก็คือ CPU นี้ ต่อกับ Memory Unit จึงเป็นสาเหตุที่ผมได้กล่าวไปว่า SSD, HDD, GPU นั้น ไม่อยู่ในส่วนที่เป็นคอมพิวเตอร์เลยนั่นเอง :) และการที่เราสามารถใช้งานอุปกรณ์เหล่านี้ได้ จริงๆ แล้วก็คือการที่ CPU อ่านเขียนลงหน่วยความจำหลักอีกที อุปกรณ์เหล่านี้จะรู้กันว่า ถ้ามีข้อมูลปรากฏขึ้นในหน่วยความจำช่วงนี้ถึงช่วงนี้ มันมีหน้าที่จะต้องเอามาประมวลผล เหมือนเป็นคอมพิวเตอร์อีกตัวที่เกาะกับคอมพิวเตอร์ตัวหลักอยู่ เพราะจริงๆ แล้ว ข้างใน SSD HDD ก็มี CPU ของมันเอง (Controller) อยู่อีกเหมือนกันนะ


ทีนี้ก็รู้แล้วสิว่า ทำไมมันต้องมี "Hardware reserved" !

การที่จะให้เป็นคอมพิวเตอร์ที่ทำงานได้โดยสมบูรณ์ ผู้ออกแบบก็จะระบุเอาไว้ว่า "ชุดคำสั่ง" (Instruction Set Architecture - ISA) ที่ตัว CPU นั้น สามารถประมวลผลตามได้ มีอะไรบ้าง เมื่อกี้ผมเองก็ได้ออกแบบ ISA ที่มีสองคำสั่งไปแล้วเป็นตัวอย่าง คือ บวก กับ ยกกำลัง :) ซึ่ง ISA ที่เราทุกคนคุ้นเคยกันอยู่ทุกวันนี้ และได้ยินกันบ่อยๆ ก็คือ x86, AMD64 และก็ ARM นั่นไง

ก่อนจะไปต่อ เรามาสรุปความรู้กันก่อนว่า:

  • CPU ประกอบด้วย CU และ ALU
  • CPU นั้น ทำงานตาม "คำสั่ง" ซึ่งถูกเก็บอยู่ในหน่วยความจำ หรือ MU
  • คำสั่งนั้น จะเป็นการสั่งให้ CPU ทำงาน ซึ่งจะเป็นการสั่ง CU ให้ไปคำนวณด้วย ALU อีกทีหนึ่ง
  • เนื่องจาก CPU สามารถเขียนข้อมูลลงไปในหน่วยความจำได้ด้วย ก็แปลว่า คำสั่งต่อไปที่มันจะทำงาน ก็อาจจะถูกเปลี่ยนด้วยคำสั่งก่อนหน้านั้นได้

ด้วยแนวความคิดเท่านี้แหละ ก็ถูกเอามาต่อยอดไปอีก จนออกมาเป็น Core i7, Ryzen แล้วก็ Snapdragon ที่เราต้องคอยนั่งดูเวลาจะซื้อคอมพิวเตอร์กันทุกวันนี้ แต่ถ้าเราจะเข้าใจได้ว่า อะไรคือ Core อะไรคือ Thread เราเลยต้องพาลไปรู้อีกเรื่องเพิ่มด้วย ก็คือ Pipeline

Pipeline

เมื่อเราพยายามเอาแนวคิดของคุณ Vonn Neumann มาทำเป็น CPU ขึ้นมาแล้ว ก็จะพบว่าเอาจริงๆ แล้วเนี่ย CU มันยังมีงานย่อยๆ แบ่งออกไปได้อีก ซึ่งงานพวกนี้ ก็จะเป็นงานแบบ Fixed Function คือทำแบบเดิมกันซ้ำๆ สำหรับทุกคำสั่ง แล้วแต่ว่าใครจะออกแบบไว้ยังไง แต่เพื่อความสะดวก เลยต้องขอยกตัวอย่างออกมา 1 คน ก็คือ สถาปัตยกรรม MIPS ซึ่งเขาออกแบบว่า สำหรับทุกคำสั่งที่ส่งเข้ามายัง CPU เนี่ย มันจะแบ่งได้เป็นขั้นตอนแบบนี้

  • IF - Instruction Fetch ก็คือไปอ่าน (Fetch) Instruction ขึ้นมาจากหน่วยความจำ โดยจะมีหน่วยความจำบนตัว CPU  เรียกว่า Program Counter Register (PC) (ซึ่ง Register ไม่ใช่ Cache นะ ใน ISA ก็จะบอกด้วยว่ามี Register แบบนี้ให้ใช้งานได้กี่ตัว) กำหนดว่า จะต้องอ่านจากหน่วยความจำช่องไหนออกมา แน่นอนว่าเราก็เขียน Instruction ไปเปลี่ยนค่าของ PC ได้ด้วย
  • ID - Instruction Decode เมื่ออ่าน Instruction มาแล้ว ก็จะต้องมีวงจรที่ทำความเข้าใจว่า ตกลงอ่านได้อะไรออกมา CPU เข้าใจคำสั่งนี้มั๊ย แล้วต้องทำอะไรบ้าง
  • EX - Execute เมื่อเข้าใจแล้วว่าจะต้องทำอะไร ก็ทำมันซะในขั้นตอนนี้ เช่น บวก ลบ คูณ หาร AND OR
  • MEM - Memory Access เป็นขั้นตอนการ "อ่าน" ข้อมูลจากหน่วยความจำหลัก ทั้งนี้ สำหรับ MIPS ISA จะไม่สามารถ "บวก" ข้อมูลในหน่วยความจำได้โดยตรง จะต้อง อ่านข้อมูลเข้ามาเก็บใน Register ก่อน แล้วการบวก จะเป็นการบวก Register ตัวนึง กับอีกตัวนึง แล้วไปเก็บที่อีกตัวนึง ตามที่คำสั่งกำหนดมา ซึ่งถ้าลองคิดตามดู ก็จะพบว่า เราเองก็บวกเลขด้วยวิธีนี้คือ เรามองสัญลักษณ์บวก มองเลขสองตัว จำเลขไว้ในหัว แล้วบวกในใจ จากนั้นมือถึงเขียนลงไป

    แล้วสงสัยมั๊ยว่า ถ้าเกิดว่าเป็นคำสั่ง บวก ซึ่งผมเพิ่งบอกไปเองว่า MIPS ISA ไม่มีการอ่านบวกหน่วยความจำช่องนึงกับอีกช่องนึง แล้วขั้นตอนนี้ของคำสั่งบวก จะทำอะไร? ก็จะไม่มีการทำงานอะไรไง! วงจรที่เกี่ยวกับ MEM ก็จะว่างงานในช่วงนี้ (นี่คือ KEYWORD เลยนะ)
  • WB - Write Back เป็นขั้นตอนการ เขียนข้อมูลจาก Register เข้าไปในหน่วยความจำ เช่นเดียวกับขั้นตอน MEM ถ้าเกิดว่า CPU กำลังดำเนินการตามคำสั่ง บวก ซึ่งไม่มีการใช้งานหน่วยความจำ วงจรที่เกี่ยวกับ WB ก็จะว่างงานในช่วงนี้เช่นเดียวกัน (นี่แค่คำสั่งบวก ว่างไปสองวงจรแล้วนะ)

เพื่อให้เห็นภาพ ให้นึกถึงสายพานในโรงงาน ที่เมื่อเราวางวัตถุดิบลงไป มันก็เคลื่่อนผ่านเครื่องจักรต่างๆ กัน 5 ตัว พอคิดถึงตรงนี้แล้วกำลังจะทำภาพ ก็เลยนึกถึงเกม Purble Place ใน Windows 7 ขึ้นมาได้ อิอิ คิดถึงจัง แหม่ เป็น 5 Stage เหมือนกันด้วย

ก็ถ้าใช้ Purble Place เป็นตัวอย่าง เค้กก็เปรียบได้กับ Instruction ที่วิ่งผ่านเครื่องจักรตำแหน่งต่างๆ ที่ทำหน้าที่บีบครีม แต่งหน้าเค้ก

แล้วพอเค้กผ่านขั้นตอนต่างๆ ก็จะได้เป็นเค้กที่เสร็จสมบูรณ์ออกมาในตอนจบ

ถ้าเรารอจนกว่าเค้ก 1 อันเสร็จ แล้วค่อยทำเค้กอีกอัน เราจะได้ประสิทธิิภาพในการทำงานที่ต่ำมาก สมมุติว่า 1 รอบการทำงาน (Cycle) ของเครื่องจักรแต่ละชิ้น คือ 1 วินาที เราก็จะได้เค้กออกมาน้อยกว่า 1 เค้้กต่อรอบการทำงาน (Instruction Per Cycle - IPC) เป็นประสิทธิภาพที่เรียกว่า Subscalar Performance คือน้อยกว่า 1 

ตัวรอบการทำงานนี้มันก็คือ "Clock Speed" (ความเร็วสัญญาณนิฬาิกา) ของ CPU นั่นเอง ซึ่งถ้าโรงงานเค้กนี้เป็น CPU มันก็คือ CPU ที่ทำงานที่ความเร็ว 1 Hz หรือ 1 ครั้งต่อวินาที แต่ CPU ของจริงตอนนี้ ทำงานกันที่ 4-5 GHz กันแล้ว ก็หมายถึงว่า 1 Cycle ของมันคือแค่เพียง 1 ในห้าพันล้านวินาที หรือ 1 วินาที ทำงานได้ 5 พันล้านครั้งทีเดียว

แล้วจะทำยังไง ให้ทุกเครื่องจักรถูกใช้งาน ก็แทนที่เราจะรอ เราก็ส่งออเดอร์เค้กเข้าไปติดๆ กันสิ ยังไงขั้นตอนมันไม่ได้เกี่ยวเนื่องกันอยู่แล้ว (ไม่มี Data Dependency ระหว่างกัน) เพื่อให้เครื่องจักรทุกเครื่องทำงานได้พร้อมกันในทุก 1 รอบการทำงานยังไงละ

ถ้าเกิดว่า 1 ขั้นตอนใช้เวลา 1 วินาที หลังจาก 5 วินาทีแรกผ่านไป เราก็จะมีเค้กออกมาทุกๆ 1 วินาทีเลย แบบนี้คือ Scalar Performance คือได้ 1 IPC = 1 Instruction Per Cycle (หรือ Clock ก็ได้)

ซึ่งภาพมาตรฐานที่ใช้สอนกัน จะเป็นภาพแบบนี้ (แบบเค้กสวยกว่าใช่ไหม :D)

Fivestagespipeline.png
CC BY-SA 3.0, Link

การทำงานแบบนี้แหละ เรียกว่า Pipelining ครับ :) โดย ISA นี้มี 5 Pipeline Stage โดยนี่คือ 1 (Hardware) Thread และการทำงานแบบนี้เรียกว่า In-Order-Execution

ถ้าสนใจอ่านต่อ CPU ที่มี Pipeline สั้นๆ แบบนี้ ส่วนมากจะเป็น RISC - Reduced Instruction Set Computer ส่วน CPU x86 นั้นเป็นแบบ CISC - Complex Instruction Set Computer แต่ CPU x86 โหดว่าคือ มันเป็น RISC ที่ Emulate CISC อีกที!!! ซึ่งอันนี้คือสาเหตุที่ว่าทำไมเรายังไม่มี CPU x86 ที่ประหยัดไฟได้เท่ากับ ARM ในมือถือ นั่นก็เพราะว่าโดย ISA แล้ว x86 เป็น CISC ทำให้คำสั่งของมันซับซ้อนกว่า วงจรการทำงานก็ซับซ้อนตามไปด้วยแล้วก็ยังมี OISC - One Instruction Set Computer และ ZISC - Zero Instruction Set Computer อีกนะ

แล้วก็ยังมีเรื่องที่เกี่ยวข้องกันอีก คือเรื่อง Pipeline Stall และการทำ Branch Prediction, รวมไปถึงว่าทำไม NetBurst Micro Architecture เมื่อ 10 ปีที่แล้ว ที่เป็น Case Study ตอนที่ผมเรียนวิชา Computer Architecture เลย มันถึงได้ช้ากว่า AMD และ Scale ได้ยาก (ส่วนนึงคือ Pipeline Stall กับ Brach misprediction Penalty นั่นแหละ) ทั้งที่เทคโนโลยีภายในตัวของ NetBurst นั้นแพรวพราวล้ำหน้ามาก ถ้าสนใจก็ไปตามอ่านกันได้เลย

เจ้าของโรงงานย่อมไม่อยากให้มีใครว่างงาน

จะเห็นว่า เวลาที่เราทำงานแบบ In Order Execution ก็คือทำไปตามลำดับเรื่อยๆ จะพบว่า มีบางคำสั่ง ที่วงจร (Functional Unit) บางส่วนไม่ได้ใช้งาน ถ้าอย่างกรณีของเกมเค้กเมื่อกี้นี้ เครื่องหมายเลข 3 จะเป็นเครื่องที่บีบครีมตรงกลางระหว่างเค้กสองชั้น ถ้าเกิดว่าเป็นเค้กชั้นเดียว หรือว่าเป็นเค้กที่ไม่ต้องการครีมระหว่างชั้น มันก็จะว่างงาน ทำให้เสียเวลาไปเปล่าๆ แล้วจะทำอย่างไรดี ถึงวงจรพวกนั้น จะถูกใช้งานอย่างเต็มที่? เราก็ต้องไม่ทำงานตามลำดับไง

หลักการก็คือ แทนที่เราจะออกแบบให้ CPU จะ Fetch มาทีละ 1 Instruction แล้วทำงานจนจบ มันก็ Fetch มาทีเดียวหลายๆ Instruction เลย แล้วด้วยเวทมนตร์ของอัลกอริธึมอันซับซ้อนบนตัว CPU นั้นมันก็จะมาหาต่อว่า จะต้องทำงาน Instruction ไหนก่อน ทำอันไหนทีหลังหลัง ถึงจะได้ใช้ Function Unit ให้มากที่สุด โดยที่ได้ผลลัพทธ์เหมือนเดิม เหมือนกับว่า Instruction ทั้งบล็อกนั้น ทำงานเรียงกันตามปกติ วิธีการแบบนี้เรียกว่า Out-Of-Order Execution, In-Order Retire หรือ OoO 

(จริงๆ แล้ว เกม Pebble Place เนี่ย เราก็ต้องทำแบบ OoO ด้วยนะ ในระดับความยากกลางและสูง!!)

อ่านถึงตรงนี้ น่าจะพอเข้าใจแล้วหลายอย่าง ขออนุญาตโยน Block Diagram ของ CPU Skylake (ซึ่ง Kaby Lake, Coffe Lake ก็ยังคงใช้สถาปัตยกรรมเดียวกันอยู่) จากเว็บ WikiChip มาให้ดูเลย (คลิกเพื่อไปดูรูปเต็มบน Wikichip)

จากในภาพจะเห็นว่า มันซับซ้อนกว่า IF ID EX MEM WB ที่เราเพิ่งดูไปเมื่อกี้มาก และจากในภาพเมื่อเทียบกับ Vonn Neumann Machine ตอนแรกแล้วก็จะเห็นว่าในส่วนที่เป็น "Execution Unit" นั้น มี ALU อยู่ถึง 4 ตัว แต่ว่ามีความสามารถไม่เท่ากัน และมี AGU ซึ่งใช้ในการเข้าถึงหน่วยความจำอีกถึง 3 ตัว ทั้งที่ CPU ตามที่ทฤษฎี มี ALU แค่ 1 ตัวก็พอแล้ว

AGU: Addregress Agreegation Unit - เนื่องจากสมัยนี้การเรียกใช้หน่วยความจำจะมีการ "บวก" เกิดขึ้นเสมอ เพราะว่ามีหลายโปรแกรมที่ใช้หน่วยความจำหลักร่วมกัน ซึ่งแต่ละโปรแกรมจะมองเห็นหน่วยความจำหลักทั้งหมด เช่น ถ้ามีแรม 16GB ก็หมายถึงโปรแกรมสามารถใช้ได้ทั้ง 16GB (หรือมากกว่า) ต่อให้มีโปรแกรมอื่นใช้อยู่ก็ตาม โดย OS จะเป็นคนจัดการทำให้มันเป็นไปได้อีกที ด้วยระบบที่เรียกว่า Paging

ส่วนที่ต้องบวก (Offset) ก็เพราะว่า โปรแกรมอาจจะขอใช้งานหน่วยความจำช่องหมายเลข 5 (Address หมายเลข 5) แต่ว่าในหน่วยความจำจริงๆ นั้น OS อาจจะแบ่งหน่วยความจำเป็นหลายๆ ช่วง (Page) ช่องที่ 5 ของโปรแกรมนี้ก็จะไม่ใช่ช่องที่ 5 จริงๆ บนหน่วยความจำ แต่ว่าจะเกิด Offset ไปก่อนจากช่วงที่โปรแกรมนี้ได้รับการจัดสรรให้

เช่น ถ้าเกิดว่ามีหน่วยความจำ 30 ช่อง OS แบ่งให้เป็น 3 ช่วง (คือมี 3 Page) ช่องที่ 5 ของโปรแกรม ซึ่งถูกวางอยู่ในช่วงที่ 3 ก็จะเป็นช่องที่ 20 + 5 = 25 ในหน่วยความจำ เป็นต้น

ขั้นสุดก็คือ Simultaneous Multithreading (SMT)

และความพยายามขั้นสุดท้ายของการใช้ Functional Unit พร้อมกันให้ได้มากที่สุด ก็คือ การให้ 1 Core นั้น สามารถรันโปรแกรมพร้อมกันหลายๆ โปรแกรมได้ (เพื่อความไม่สับสนต่อไปนี้ 'เธรด' คือ Software Thread หรือ เธรดที่สร้างจากโปรแกรม ส่วน Thread คือ Hardware Thread นะ)

เนื่องจากว่าเวลาที่เราสร้างเธรดขึ้นมาในโปรแกรมของเรานั้น ไม่จำเป็นเสมอไปว่า เธรดนั้นจะทำงานอยู่ตลอดเวลา ด้วยปัจจัยหลายๆ อย่าง เช่น การเรียกใช้งานหน่วยความจำ ซึ่งทำงานช้ากว่า CPU มาก ทำให้เธรดนั้นต้องรอข้อมูลจากหน่วยความจำมาถึงก่อน จึงจะสามารถทำงานต่อได้ หรือการเรียกใช้ทรัพยากรบางอย่างของระบบ ซึ่งเธรดนั้นก็ต้อง หลับรอ (Sleep) ไว้เหมือนกัน หรือแม้กระทั่งการนัดแนะกันระหว่างเธรด (Thread Synchronization) เพื่อไม่ให้ทำงานชนกัน ก็ต้องเกิดการหลับรออีกด้วย จังหวะที่เธรดนี้หลับอยู่ Functional Unit ภายในตัวของ CPU ก็จะว่างลง เป็นโอกาสที่อีกเธรดหนึ่ง สามารถเอามาใช้งานได้ จึงเป็นที่มาของเทคนิค Simulteneous Multithreading (SMT) คือ ให้ CPU 1 Core สามารถรับ Instruction จากเธรดมากกว่า 1 เธรดได้เลย แล้วให้ระบบ Out-of-Order Execution ทำงานจัดสรรทรัพยากรที่ว่างลงจากการที่เธรดเกิดการรอ ให้กับเธรดอื่นที่ยังพร้อมทำงานแทน

แต่ที่นี้ถ้าเกิดว่าอ่านมาถึงตรงนี้ อาจจะสงสัยว่า เอ แล้วจะเป็นไปได้ยังไงที่ CPU จะสามารถทำงานหลายโปรแกรมพร้อมกันได้ เนื่องจากว่าทุก Instruction จะต้องมีการเขียนข้อมูลลง Register แล้วทั้งสองโปรแกรม (Process) ก็ไม่รู้จักกัน แถมในมุมมองของโปรแกรมแล้ว คือมีโปรแกรมเดียวที่รันอยู่บน CPU อีก นั่นคือโปรแกรมสามารถใช้ Register ได้ครบทุกตัวตาม ISA ระบุไว้ แล้วแบบนี้โปรแกรมจะไม่เขียน Register ทับกัน หรือไปอ่าน Register ของอีกโปรแกรมมาได้เหรอ?

ตรงนี้แหละคือที่มาของการระบุว่า Core นี้ จะมีกี่ Thread ครับ

สมมุติถ้าเราจะกำหนดว่า Core นี้มี 2 Thread เราก็จะต้องออกแบบตัว Core (ในภาพ Block Diagram ที่แสดงให้ดูคือ 1 Core) ให้มีพื้นที่สำหรับเก็บ Register ทั้งชุดของ ISA ได้จำนวน 2 ชุด เพื่อให้เหมือนว่า สองโปรแกรมนั้น ต่างคนต่างมี Core ของตัวเอง ทั้งที่จริงๆ แล้ว Core นี้ มีการใช้ ALU, AGU ร่วมกัน

 

และไม่จำเป็นว่า Core เดียว จะต้องมีแค่ 2 Thread เสมอไป เราจะทำให้ Core เดียวมีมากกว่า 2 Thread ก็ยังได้ โดยสูงสุดที่เคยมีการออกแบบขึ้นมาคือ มีถึง 128 Thread ต่อ Core กันเลยทีเดียว แต่ว่าเทคนิคการกระจาย Instruction จะต่างกับ SMT ที่ผมพูดถึงอยู่ในบล็อกนี้นะ

ทีนี้อย่าเพิ่งเข้าใจว่า โปรแกรมเปิดขึ้นมาแล้วจะอยู่ Thread เดิม บน Core เดิมไปตลอดนะ (ยกเว้นตั้งค่า Affinity ใน Windows ซึ่งจะล็อคให้โปรแกรมอยู่ Core เดิมเสมอได้) เพราะว่าเป็นหน้าที่ของ OS ว่า ในจังหวะเวลาแต่ละช่วงนั้น โปรแกรมไหนและเธรดของโปรแกมนั้น จะไปวางอยู่บน Thread อะไร ของ Core ไหนบ้าง การที่ CPU เรามี 12 Thread แปลว่า OS สามารถจัดสรรให้เธรด ทั้งหมด 12 เธรด สามารถทำงานพร้อมกันได้ โดยที่ไม่จำเป็นว่า 12 เธรดนั้น จะต้องมาจากคนละโปรแกรมหรือเป็นของโปรแกรมเดียวกันทั้งหมดด้วยนะ

การที่จะใช้ Thread ให้คุ้มค่าที่สุด OS จะต้องรู้จักด้วยว่า CPU ตัวนี้ เป็น 2 Thread/Core เพื่อที่ว่าจะได้จัดสรรให้เธรดจากโปรแกรม เดียวกัน ให้ไปทำงานที่ Core เดียวกันด้วย เนื่องจากแต่ละ Core ก็จะมีข้อมูลที่พักไว้จากหน่วยความจำ (L1, L2 Cache) ของตัวเองแยกกันต่างหาก ถ้าเกิดว่า OS ทำการวางเธรดจากโปรแกรมเดียวกันไปบนหลาย Core หรือว่าเดี๋ยวอยู่ Core นู้นที Core นี้่ที ก็จะกลายเป็นว่าเธรดนั้น จะต้องโหลดข้อมูลจากหน่วยความจำเดิมซ้ำอีกรอบทุกครั้งที่มันได้โอกาสทำงานบน CPU (เลยเป็นเหตุที่เดี๋ยวนี้มี L3 Cache ซึ่งจะเป็น Cache ที่ทุก Core ใช้ร่วมกัน) แถมการให้เธรดไปอยู่หลาย Core มันก็จะไปทำให้มีข้อมูลบน Cache อยู่ข้าม Core กันอีก ก็ต้องมาจัดการต่ออีกว่า ตกลงใครมีข้อมูลล่าสุด (Cache Coherency)

เอาละก่อนไปต่อ มาสรุปความเข้าใจกันอีกรอบดีกว่า :)

  • Core คือ CPU 1 ตัว มี Functional Unit ในตัวครบถ้วนสมบูรณ์ สามารถทำการประมวลผลคำสั่งได้
  • เพื่อให้การใช้งาน Functional Unit เต็มที่ที่สุด ความพยายามแรกคือเราใช้เทคนิค Pipelining คือพยายามแบ่ง CPU ออกเป็น Functional Unit เล็กๆ ให้มันทำงานพร้อมกันกับหลาย Instruction ที่ส่งเข้ามา
  • Out-of-Order Execution คือเทคนิคต่อยอดจาก Pipelining ให้ CU ใน CPU จัดสรรได้อีกว่า Instruction ไหนควรทำก่อนหลัง แทนที่จะเป็นตามลำดับไปแบบ Pipeline
  • ในทางทฤษฎี ตัว CPU 1 Core ก็คือมี 1 Thread เนื่องจากมันสามารถประมวลผลเธรดของโปรแกรมได้ครั้งละ 1 เธรด
  • Simultaneous Multithreading คือ เทคนิคในการเพิ่มการใช้งาน Functional Unit ให้เต็มที่ที่สุด ที่ต่อยอดจาก Out-of-Order Execution ไปอีก โดยการเพิ่มจำนวน Register ทั้งชุดของ ISA ขึ้น ตามจำนวน Thread ที่ต้องการให้ Core นั้นรองรับ เพื่อให้ Functional Units สามารถทำงานประมวลผล Instruction จากหลายเธรด พร้อมกันได้
  • Thread จริงๆ แล้วไม่มีอยู่เลยใน Core มันเป็นแค่การสมมุติขึ้นมา พอ CPU มี 2 Thread จึงเรียกว่ามี 2 Logical Processor ใน 1 Physical Processor ยังไงละ

แล้วการแค่เพิ่ม Register ขึ้นมาอีก 1 ชุด มันช่วยเพิ่มประสิทธิภาพได้มากแค่ไหน?

แปลกมากเลยที่แค่การเพิ่ม Register ขึ้นมาแค่อีก 1 ชุด ทำให้เพิ่มประสิทธิภาพได้ แสดงให้เห็นว่าตอนนี้ CPU มันทำงานเร็วมากจริงๆ ขนาดแค่ใช้หน่วยความจำ Function Unit ในตัวมันก็ยังว่างให้ไปเอา Instruction จากอีกเธรดมาทำงานได้ แต่มันจะมากขึ้นขนาดนั้นเลยเหรอ?

แน่นอนว่า สำหรับคำถามเรื่องประสิทธิภาพทั้งหมด ไม่มีใครสามารถให้คำตอบได้แน่ชัด และมีตัวแปรสำคัญคือ โปรแกรมที่เอามาใช้ทดสอบประสิทธิภาพเองด้วย ที่ยากที่สุดคือ ALU นั้นก็มีความสามารถไม่เท่ากัน การที่จะคำนวณออกมาโดยไม่ทดสอบทำงานจริงนั้น แทบจะเป็นไปไม่ได้เลย เพราะว่าตัวที่จัดคิวงานบน CPU (Scheduler) นั้นมันจะพยายามหาทางเติมงานให้ทุก Functional Unit ใช้งาน จากงานที่มี ณ ขณะนั้น ซึ่งผลที่ได้ออกมาก็จะแปรผันตามงานที่ส่งเข้าไปให้มันจัดคิวงาน

สำหรับเทคโนโลยี SMT ของ Intel นั้น (HyperThreading) ทาง Intel เคลมว่า ได้ประสิทธิภาพมากขึ้นกว่า 15-30% เมื่อเทียบกับ CPU จำนวน Core เท่ากัน ที่ไม่มี Hyper Threading ซึ่งถ้าเกิดว่าลองดูจากผลทดสอบของ i5 เทียบกับ i7 ซึ่งมักจะมีจำนวน Core เท่ากัน แต่ว่า i7 มี Hyper Threading เกือบทุกรุ่น ก็จะพบว่า ประสิทธิภาพของ i7 นั้น จะสูงกว่าประมาณ 30% อยู่เสมอเลย ก็เป็นไปได้ว่าอาจจะจริงอยู่นะ

ขอเอาผลจากเว็บ PassMark มาเทียบกันก่อนนะ แต่ก็ไม่แน่ใจเหมือนกันว่าผลคลาดเคลื่อนขนาดไหน เพราะว่าอาจจะถูก Overclock กันหมดเลยก็ได้ เพราะเป็น CPU K เอาเป็นจุดอ้างอิงพอประมาณแล้วกันนะ


8600K vs 8700K ต่างกัน 24%save
(Default เมื่อใช้งานครบ 4 Core 8600K Turbo ที่ 4.1GHz, 8700K อยู่ที่ 4.3GHz สูงกว่า 5%
โดยประมาณแล้วถ้า Clock Speed เท่ากัน ควรต่างกัน 20%)


7600K vs 7700K ต่างกัน 31%
(Default เมื่อใช้งานครบ 4 Core 7600K Turbo ที่ 4.0GHz, 7700K อยู่ที่ 4.4GHz สูงกว่า 10%
โดยประมาณแล้ว ถา้ Clock Speed เท่ากัน ควรต่างกัน 20%)


6600K vs 6700K ต่างกัน 37%
(Default เมื่อใช้งานครบ 4 Core 6600K Turbo ที่ 3.6GHz, 6700K อยู่ที่ 4.0GHz สูงกว่า 10%
โดยประมาณแล้ว ถา้ Clock Speed เท่ากัน ควรต่างกัน 27%)

แต่ที่น่าสนใจกว่า คือ ระหว่าง i3-8350K ซึ่งเป็น 4 Core / 4 Thread เมื่อเทียบกับ i5-8600K ซึ่งมี 6 Core / 6 Thread หรือมัีจำนวน Core เพิ่มขึ้น 50% ประสิทธิภาพที่เพิ่มขึ้น อยู่ที่ประมาณ 38% 


8350K vs 8600K ต่างกัน 38%
(Default เมื่อใช้งานครบ 6 Core 8600K Turbo ที่ 4.1 สูงกว่า i3 อยู่ 2.5%)

และที่น่าสังเกตก็คือ i7-9700K เลือกที่จะเพิ่มจำนวน Core ขึ้น 33% จาก 6 Core เป็น 8 Core แต่ว่าไม่เปิดใช้ HT ทำให้ Thread มันมีน้อยลง 33% ไปด้วย หรือมองในมุมกลับกันก็คือ i7-8700K ซึ่งมี 6 Core แต่ว่ามี HT ก็อาจจะมีประสิทธิภาพใกล้เคียงกับ i7-9700K พอดี เพราะว่า เทคโนโลยี HT ควรจะเพิ่มประสิทธิภาพให้ 8700K ได้ประมาณ 30%

ผลทดสอบโดย Passmark ก็เป็นแบบนั้นพอดีเลย


8700K vs 9700K ต่างกัน 8.6%
(Default เมื่อใช้งานครบทุก Core 8700K Turbo ที่ 4.3GHz, 9700K อยู่ที่ 4.7GHz สูงกว่า 9%)

แต่ว่าก็ต้องอย่าลืมว่า อีกส่วนหนึ่งที่จะมีผลต่อประสิทธิภาพก็คือความเร็ว (GHz) ของตัว Core เองด้วย โดย 9700K มีความได้เปรียบที่ Clock Speed สูงกว่า 9% และมันก็ได้คะแนะมากกว่า ประมาณ 9% พอดีเป๊ะ

สรุปว่า ต้อง Core เยอะ หรือ Thread เยอะดีกว่า?

แน่นอนว่าผมเองก็โคตรจะสงสัยเหมือนกัน ว่ามันมีผลแค่ไหนกันนะ เลยลองไปเอาคะแนน Passmark มาทำเป็นกราฟ เปรียบเทียบกับ G4520 ซึ่งเป็น CPU 2 Core ความเร็ว 3.6GHz ออกมาว่า ประิมาณ Core/Thread ที่เพิ่มขึ้นโดยไม่สนใจความเร็วที่เพิ่มขึ้นนั้น มันมีผลกับคะแนนซักแค่ไหน ผลก็ได้ออกมาตามนี้ (หัวกราฟผิดนะ แหะๆ) ซึ่งน่าจะใกล้เคียงกับสิ่งที่คนซื้อคาดหวัง คือ ถ้า 8 Core มันก็ควรจะเป็น 4 เท่าของ 2 Core และ 6 Core ควรจะเป็น 3 เท่าของ 2 Core ไม่แน่ว่า Intel อาจจะคิดมาแล้วด้วยหรือเปล่า ถึงได้ตั้ง Clock Speed มาให้มันได้ผลตามนี้

แล้วถ้าสมมุติว่าผลของ Clock Speed ต่อคะแนน มีความสัมพันธ์กันแบบ 1:1 (ซึ่งมันไม่จริงนะ อันนี้ทำเล่นๆ ในชิพคนละ Generation กันมันเทียบกันไม่ได้อยู่แล้วด้วยละ) ก็จะได้แบบผลซึ่งค่อนข้างใกล้เคียงกับความเป็นไปได้ทางทฤษฎีมากกว่าด้วย เนื่องจากการเพิ่มเธรด (เราตกลงกันไว้ว่า เธรดคือ Software Thread นะ) การทำงานนั้นเนี่ย จะไม่ได้ให้ผลลัพธ์ที่เพิ่มแบบ 1:1 เพราะว่าจะต้องไปเสียเวลาทำ Synchronization กันเพิ่มขึ้น เหมือนที่เราเอาการ์ดจอ 2 ใบ SLI กัน ความเร็วก็ไม่ได้มากขึ้นเป็นสองเท่าเสมอไป 

จากสองกราฟ เราก็เลยพอจะสรุปได้ว่า

  • จำนวน Core มีผลกับประสิทธิภาพมากที่สุด แทบจะเท่ากับจำนวน Core ที่เพิ้่มขึ้น แต่ถ้าตัดเรื่องความเร็วที่เพิ่มขึ้นของชิพรุ่นใหม่ออกไปด้วย (แบบมั่วๆ ย้ำอีกที) อัตราการเพิ่มมันก็ลดลงอยู่เหมือนกัน เช่น 2C->4C นั้นเพิ่ม 100% แต่จาก 4->6 เพิ่ม แค่ 34% และจาก 6->8 เพิ่่มขึ้นเพียง 20%
  • การทำ SMT (HyperThread) ก็มีีผลประมาณ 15-20% ตามที่ Intel บอกไว้จริงๆ และด้วยอัตราการเพิ่มที่น้อยลง การทำ SMT อาจจะดีกว่าการเพิ่ม Core ด้วย เนื่องจากประหยัดกว่า ความร้อนเพิ่มน้อยกว่า เพราะว่าเป็นแค่การเพิ่ม Register เข้าไปอีก 1 ชุด โดย Intel เคลมว่า ใช้พื้นที่บนชิพเพิ่มอีกแค่ 5% แต่ได้ประสิทธิภาพเพิ่มขึ้น 15-20%

ส่งท้าย...และผลการทดสอบ

ของเราเอง ก็มีผลการทดสอบออกของเราเอง ระว่าง i7-8700K และ i7-9700K กับเครื่อง NXL และ i9-9900K ในเครื่อง XM15 อยู่ด้วยเช่นเดียวกัน เดี๋ยวจะมาเล่าให้ฟังในอีกโพสนึงนะครับ

หวังว่าโพสนี้ น่าจะได้ความรู้เต็มเปี่ยม และอ่านสนุกไม่เบื่อ ถ้ามีอะไรอยากแนะนำ ก็อย่าลืมมาคอมเม้นต์บอกกันไว้บ้างนะ

BLOG