How does GoLang know how many CPUs to use?

English 简体中文 繁体中文 ภาษาไทย Tiếng Việt
Summary

Go's `runtime.NumCPU()` and `GOMAXPROCS` report the number of CPUs allocated to the current process, not necessarily the total physical CPUs on the system. This value, `ncpu`, is determined during process startup via `runtime/os_linux.go`'s `osinit()` function, which calls `getproccount()`. Internally, `getproccount()` leverages `sched_getaffinity()` (an assembly-implemented native function) to read the process's CPU affinity mask. While typically matching the machine's total CPUs by default, this count reflects the OS-allocated resources. Developers can explicitly limit the number of CPUs a Go process utilizes by configuring cgroups, specifically using `cpuset` to restrict the process's CPU affinity.

เมื่อเรียกใช้คำสั่ง lscpu บน Linux มันจะแสดงรายการข้อมูล CPU บนเครื่อง ยกตัวอย่างเช่น มี CPU หนึ่งตัวที่มี 2 คอร์ และแต่ละคอร์มีสองเธรด ซึ่งหมายความว่ามี 4 คอร์ที่พร้อมใช้งาน

ตอนนี้มาดูกันว่าโปรแกรม GoLang จะระบุจำนวนคอร์ได้เท่าไหร่

จากผลลัพธ์ NumCPU และ GOMAXPROCS ทั้งคู่แสดงผล 4 ซึ่งเป็นไปตามที่คาดไว้ Go runtime ได้ข้อมูลนี้มาอย่างไร มันได้มาจากคำสั่งที่คล้ายกับ lscpu หรือ /proc/cpuinfo หรือไม่? มาลองดูใน source code ของ GoLang กัน

ใน runtime/debug.go มันบอกว่าค่านี้มาจากตัวแปร global ncpu ซึ่งถูกตั้งค่าเมื่อกระบวนการเริ่มต้นและจะไม่เปลี่ยนแปลงหลังจากตั้งค่าแล้ว

ncpu ถูกตั้งค่าที่ไหน? ค้นหาคำค้นหานี้ด้วย grep ในไดเร็กทอรี runtime ใน runtime/os_linux.go มีฟังก์ชัน osinit() ซึ่งเรียก getproccount() เพื่อรับค่า ncpu

ภายใน getproccount() มันเรียก sche_getaffinity() ซึ่งจะมีผลลัพธ์ buf โดยแต่ละบิตที่มีค่าเป็น 1 จะถือว่าเป็นหนึ่งคอร์

ฟังก์ชัน sche_getaffinity() เป็นฟังก์ชันเนทีฟที่กำหนดด้วย go assembly การใช้งานสามารถพบได้ใน runtime/sys_linux_amd64.s

จากข้างต้น ค่าของ ncpu ไม่เหมือนกับผลลัพธ์จาก lscpu มันคือจำนวน CPU ที่ OS จัดสรรให้กับกระบวนการปัจจุบัน ในกรณีส่วนใหญ่ คุณจะเห็นว่า ncpu เหมือนกับจำนวน CPU ที่มีอยู่ใน OS นั่นเป็นเพราะ OS จะอนุญาตให้กระบวนการหนึ่งใช้ CPU ทั้งหมดที่มีอยู่ได้โดยค่าเริ่มต้น ถ้าต้องการจำกัดจำนวน CPU ที่จะใช้โดยกระบวนการหนึ่ง สามารถใช้ cpuset จาก cgroup เพื่อทำการเปลี่ยนแปลง

ตัวอย่างเช่น มาสร้าง cgroup ชื่อ gocpu กัน

cgcreate -g cpuset:/gocpu

ถ้าต้องการอนุญาตให้ใช้เฉพาะ CPU 0-1 โดย cgroup gocpu สามารถเรียกใช้

#echo '0' > cpuset.mems
#echo '0-1' > cpuset.cpus
#echo $$ > tasks

จากนั้นให้เชลล์ bash เรียกใช้ใน cgroup ใหม่นี้ และเรียกใช้โปรแกรม go อีกครั้ง ตอนนี้มันจะมีเพียง 2 คอร์ที่จะใช้

อ้างอิง: https://www.toutiao.com/i6872354717305930254/

CPU GOLANG NCPU

  RELATED

  COMMENTS

0

No comment for this article.