5.4 ניטור וניהול NVML ו nvidia smi הרצאה
לאורך פרק 5 בנינו את מחסנית התוכנה שרצה בצד הhost (host, ה-CPU): ראינו את ה-CUDA Runtime API ואת libcudart.so שמממש אותו, את ה-CUDA Driver API ואת libcuda.so, ואת ה-NVIDIA GPU Driver שמדבר עם החומרה בפועל. כל אלה נמצאים על מסלול החישוב - הם משיקים kernels ומזיזים זיכרון. בשיעור הזה נעבור למסלול השני, המקביל, שנמצא באותה שכבת host software אבל אחראי על משהו אחר לגמרי: הניטור והניהול (monitoring and management) של מצב ה-GPU. כמה חשמל הכרטיס מושך ברגע זה? מה הטמפרטורה שלו? כמה מהזיכרון מוקצה? האם הוא מווסת (throttling) בגלל תקרת הספק או חום? בשיעור נכיר את NVML (NVIDIA Management Library) - הספרייה עם ה-C API היציב שכל הכלים נשענים עליו - ואת nvidia-smi, כלי שורת-הפקודה המפורסם שיושב מעליה. נלמד לתשאל את הכרטיס, לצפות בטלמטריה חיה תוך כדי ריצת kernel, לשנות הגדרות כמו תקרת הספק ומצב מתמיד, ולמה אסור לגרד (scrape) את הפלט הטקסטואלי של nvidia-smi בכלים אמיתיים. הרעיון של החומרה כמערכת עם תקציב הספק וחום, שנגענו בו בשיעור 0.2, יחזור כאן ככוח שיכול לחסום ביצועים - נושא שנעמיק בו בפרק 7.1.
ספריית הניהול - NVML¶
הדבר הראשון שחשוב להפריד: עד עכשיו כל ה-.so שראינו היו על מסלול החישוב. libcudart.so = ה-Runtime, libcuda.so = ה-Driver. עכשיו נכנס שחקן שלישי בשכבת ה-host software, ולו תפקיד שונה לחלוטין: NVML - NVIDIA Management Library, ספריית הניהול של NVIDIA. NVML היא, בלשון הגלוסרי, "ספרייה לניטור וניהול של מצב ה-GPU של NVIDIA". היא אינה משיקה kernels ואינה מקצה זיכרון גלובלי - היא קוראת ומשנה טלמטריה והגדרות של הכרטיס.
מבחינת מיקום במחסנית, NVML יושבת בין ה-NVIDIA GPU Driver מתחת לבין הכלים והinterfaces מעל: היא מדברת עם ה-driver כדי לקרוא ולנהל את מצב ה-GPU, וחושפת את היכולות האלה כלפי מעלה. על לינוקס היא ממומשת על ידי הקובץ libnvml.so (בדיוק כמו ש-libcudart.so מממש את ה-Runtime API), וזוהי ספריית C סגורה (closed source).
האטריביוטים המרכזיים ש-NVML חושפת - אלה שנשתמש בהם שוב ושוב:
| אטריביוט | מה זה מודד | דוגמה על H100 SXM |
|---|---|---|
| power draw | הספק רגעי שהכרטיס מושך, בוואטים | 78 W בסרק, עד ~700 W בעומס |
| temperature | טמפרטורת ה-GPU, במעלות צלזיוס | 34 מעלות בסרק, 60-75 בעומס |
| allocated memory | כמה מה-VRAM מוקצה כרגע | 512 MiB מתוך 80 GiB |
| power limit | תקרת ההספק הנוכחית (ניתנת לשינוי) | 700 W (ברירת מחדל) |
| power limiting state | האם הכרטיס מווסת כרגע, ולמה | Active: SW Power Cap |
| clocks | תדרי ה-SM והזיכרון | 1980 MHz (SM), 2619 MHz (mem) |
נקודת המפתח של השיעור כולו: NVML היא ה-API היציב לבנות עליו כלים. כל כלי ניטור אמיתי - dashboard, exporter ל-Prometheus, מתזמן שמחליט על אילו GPU להשיק job - צריך לפנות ל-C API של NVML (או לאחת מהמעטפות שלה), ולא לפרסר טקסט. נחזור לזה בהרחבה בסעיף על יציבות.
מי צורך את NVML? שלושה ערוצים: (1) nvidia-smi, כלי שורת-הפקודה, שהוא נקודת הגישה האנושית העיקרית; (2) ישירות דרך ה-C API של libnvml.so, לתוכניות C/C++; (3) דרך מעטפות שפה - pynvml ל-Python ו-nvml_wrapper ל-Rust. נראה את שלושתם.
כלי שורת-הפקודה - nvidia-smi¶
מעל NVML יושב nvidia-smi - NVIDIA System Management Interface, interface ניהול המערכת של NVIDIA. בלשון הגלוסרי הוא "כלי שורת-הפקודה לתשאול וניהול מצב ה-GPU שנחשף על ידי ספריות הניהול NVML". במילים אחרות: nvidia-smi הוא עטיפת CLI דקה מעל NVML. כל מספר שהוא מציג, וכל הגדרה שהוא משנה, עוברים דרך libnvml.so. הפלט שלו כה מוכר למשתמשי GPU עד שהגלוסרי מציין בחיוך שהוא נהפך ל"מם".
הרצה יבשה של nvidia-smi מדפיסה את הטבלה הקלאסית:
$ nvidia-smi
+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 550.54.15 Driver Version: 550.54.15 CUDA Version: 12.4 |
|-----------------------------------------+------------------------+----------------------+
| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC |
| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. |
| | | MIG M. |
|=========================================+========================+======================|
| 0 NVIDIA H100 80GB HBM3 On | 00000000:1B:00.0 Off | 0 |
| N/A 34C P0 78W / 700W | 512MiB / 81559MiB | 0% Default |
| | | Disabled |
+-----------------------------------------+------------------------+----------------------+
בואו נפרק את השדות החשובים, כי כל אחד מהם מגיע היישר מ-NVML:
- Persistence-M - מצב מתמיד (persistence mode). כשהוא
Off, ה-driver נטען ונפרק בכל פעם שאין תהליך CUDA פעיל, מה שמוסיף השהיית אתחול של מאות מילישניות לlaunch הראשונה.Onמשאיר את ה-driver טעון תמיד. - Temp - טמפרטורת ה-GPU.
N/Aתחת Fan כי כרטיסי SXM בשרת מקוררים על ידי המארז, אין להם מאוורר עצמאי. - Perf - מצב הביצועים (performance state), מ-
P0(הכי גבוה, בוסט מלא) עדP12(חיסכון עמוק). תחת עומס נצפהP0. - Pwr:Usage/Cap - שני מספרים: ההספק הרגעי (78W) מול תקרת ההספק (700W). היחס ביניהם הוא הסיפור המרכזי של השיעור: כשהעומס דוחף את ה-Usage אל ה-Cap, הכרטיס מתחיל לווסת תדרים.
- Memory-Usage - הזיכרון המוקצה מול הכולל (512MiB מתוך ~80GiB).
- GPU-Util - אחוז הזמן שבו רץ kernel כלשהו על הכרטיס. שימו לב למלכוד: זהו אחוז זמן העסוק, לא אחוז ניצול יחידות החישוב. 100% פירושו "משהו רץ כל הזמן", לא "כל ה-CUDA Cores עובדים". להערכת ניצול אמיתי צריך profiler (פרק 9.1).
- Compute M. - מצב החישוב (compute mode):
Default(ריבוי תהליכים),Exclusive_Process, אוProhibited. - MIG M. - האם ה-Multi-Instance GPU פעיל (נגיע לזה בסעיף נפרד).
תשאול - query, dmon ו-pmon¶
הטבלה יפה לעין אדם, אבל לסקריפטים היא חסרת ערך. לתשאול ממוקד יש שלושה מנגנונים.
המנגנון הראשון - --query-gpu עם פורמט CSV. אנחנו מבקשים בדיוק את השדות שאנחנו רוצים, ומקבלים שורות מכונה:
nvidia-smi --query-gpu=name,power.draw,power.limit,temperature.gpu,utilization.gpu,memory.used,memory.total,clocks.sm,clocks.mem,pstate \
--format=csv
name, power.draw [W], power.limit [W], temperature.gpu, utilization.gpu [%], memory.used [MiB], memory.total [MiB], clocks.current.sm [MHz], clocks.current.memory [MHz], pstate
NVIDIA H100 80GB HBM3, 78.11, 700.00, 34, 0, 512, 81559, 345, 2619, P0
הדגל --format=csv,noheader,nounits נותן ערכים נקיים בלי כותרת ובלי יחידות - נוח לפרסור מהיר בסקריפט. עדיין, כפי שנראה, זה לא הכלי הנכון לכלי production; זה נוח ל-one-liner אינטראקטיבי.
המנגנון השני - -q (query מלא), שמדפיס דוח מפורט של כל מה ש-NVML יודעת על הכרטיס. אפשר לצמצם לתחום מסוים עם -d. במיוחד שימושי הוא תחום הביצועים, שחושף את סיבות הוויסות (throttle reasons):
Clocks Event Reasons
Idle : Not Active
Applications Clocks Setting : Not Active
SW Power Cap : Active
HW Slowdown : Not Active
HW Thermal Slowdown : Not Active
HW Power Brake Slowdown : Not Active
Sync Boost : Not Active
SW Thermal Slowdown : Not Active
השורה SW Power Cap : Active היא בדיוק ה-power limiting state מ-NVML: הכרטיס מוריד תדרים כרגע כי הוא נגע בתקרת ההספק. זהו האות הישיר לכך שה-workload power-bound ולא compute-bound טהור.
המנגנון השלישי - טלמטריה חיה בקצב של שנייה לשורה. nvidia-smi dmon נותן ניטור device-level (לכל הכרטיס), ו-pmon נותן ניטור per-process (לכל תהליך):
הדגל -s puct בוחר קבוצות: p=power/temp, u=utilization, c=clocks, t=PCIe throughput. -d 1 = דגימה כל שנייה. הפלט תוך כדי ריצת kernel כבד:
# gpu pwr gtemp mtemp sm mem enc dec mclk pclk
# Idx W C C % % % % MHz MHz
0 79 35 - 0 0 0 0 2619 345
0 690 58 - 99 43 0 0 2619 1980
0 700 64 - 99 45 0 0 2619 1830
0 700 69 - 99 45 0 0 2619 1755
ראו את הסיפור מתגלגל: בשורה הראשונה סרק (79W, תדר SM 345 MHz). ברגע שה-kernel עולה, ההספק קופץ ל-690W ותדר ה-SM לבוסט מלא 1980 MHz. אבל בשורות הבאות ההספק נוגע ב-700W, ותדר ה-SM יורד ל-1830 ואז 1755 - זהו הוויסות בזמן אמת. ה-pmon, במקביל, מראה איזה תהליך אחראי:
ניהול - שינוי מצב הכרטיס¶
עד כה רק קראנו. אבל nvidia-smi יכול גם לשנות מצב, וכל שינוי כזה עובר דרך פונקציית set של NVML. רוב הפעולות דורשות הרשאות root ומצב מתמיד פעיל. הנה הפעולות המרכזיות:
| פעולה | דגל | מה היא עושה |
|---|---|---|
| מצב מתמיד | -pm 1 |
משאיר את ה-driver טעון, מבטל השהיית אתחול |
| מצב חישוב | -c 2 |
קובע compute mode (0=Default, 2=Prohibited, 3=Exclusive_Process) |
| תקרת הספק | -pl 500 |
מגדיר את תקרת ההספק ל-500W |
| שעונים קבועים | -lgc 1500,1500 |
נועל את תדר ה-GPU (lock GPU clocks) |
| שעוני זיכרון | -lmc 2619 |
נועל את תדר הזיכרון (lock memory clocks) |
| שעוני אפליקציה | -ac 2619,1980 |
קובע application clocks (mem,graphics) |
| איפוס | -r |
מבצע reset מלא לכרטיס |
לדוגמה, זרימת ההכנה הטיפוסית לפני benchmark - קיבוע תנאים כדי שהמדידות יהיו יציבות:
sudo nvidia-smi -pm 1 # persistence mode On
sudo nvidia-smi -lgc 1980,1980 # lock SM clock to 1980 MHz (no variable boost)
# ... run the benchmark with repeated measurements ...
sudo nvidia-smi -rgc # release the clock lock (reset GPU clocks)
למה נועלים תדרים לפני benchmark? כי אחרת התדר "צף": הכרטיס מתחיל בבוסט 1980 MHz, מתחמם, נוגע בתקרת ההספק, ומוריד תדר - ואותו kernel בדיוק ייתן GFLOP/s שונה בהרצה הראשונה מול העשירית. נעילת התדר מסירה את המשתנה הזה. נעמיק במתודולוגיית מדידה נכונה בפרק 9.1.
גוטצ'ה של תקרת ההספק. אי אפשר לקבוע תקרה שרירותית. לכל כרטיס יש טווח מותר (constraints); ל-H100 SXM זה בערך 200W עד 700W. nvidia-smi -q -d POWER מראה את Min Power Limit ו-Max Power Limit. ניסיון לקבוע מחוץ לטווח יידחה. בנוסף, על כרטיסי ענן/data-center משותפים ההרשאה לשנות תקרה לרוב חסומה - הפקודה תיכשל ב-Insufficient Permissions, וזה תקין.
פלט XML ולמה לא לגרד טקסט¶
הגענו לנקודה המושגית החשובה ביותר של השיעור, וזו נקודה שהגלוסרי מדגיש במפורש: פורמט הפלט הטקסטואלי של nvidia-smi אינו מובטח כיציב. הטבלה היפה שראינו בתחילת השיעור עלולה להשתנות בין גרסאות driver - סדר שדות, רוחב עמודות, מחרוזות. הציטוט המדויק מהמקור: "אף שפורמט הפלט הטקסטואלי של nvidia-smi אינו מובטח כיציב, ספריית ה-C של NVML מציעה API יציב לפיתוח כלים".
מכאן מסקנה מעשית חד-משמעית עם שלוש רמות של איכות:
Worst ─► nvidia-smi | grep | awk (parses free-form text - fragile, breaks on driver update)
OK ─► nvidia-smi -x (structured XML) or --query-gpu=...,--format=csv
Best ─► C API of NVML / pynvml / nvml_wrapper (the stable API, directly)
הפלט ב-XML (-x) הוא ביניים סביר - הוא לפחות מובנה, עם תגיות, אז פרסור שלו לא ישבר בגלל רוחב עמודה:
<?xml version="1.0" ?>
<nvidia_smi_log>
<gpu id="00000000:1B:00.0">
<product_name>NVIDIA H100 80GB HBM3</product_name>
<gpu_power_readings>
<power_draw>78.11 W</power_draw>
<current_power_limit>700.00 W</current_power_limit>
</gpu_power_readings>
<temperature>
<gpu_temp>34 C</gpu_temp>
</temperature>
</gpu>
</nvidia_smi_log>
אבל התשובה האמיתית לכלי production היא לדלג על nvidia-smi לגמרי ולפנות ל-C API של NVML. nvidia-smi הוא כלי אינטראקטיבי לבני אדם; הכלי שלכם צריך לדבר עם הספרייה שהוא עצמו מדבר איתה. זו בדיוק הסיבה שקיימות המעטפות pynvml ו-nvml_wrapper.
מעטפות שפה - pynvml ו-nvml_wrapper¶
ל-Python יש pynvml (חבילת PyPI בשם nvidia-ml-py), שהיא binding דק וישיר מעל ה-C API של NVML - כל פונקציה nvmlDeviceGet... נחשפת כפונקציית Python. ל-Rust יש nvml_wrapper. שתיהן נותנות את היציבות של NVML בלי לפרסר טקסט.
הנה סקריפט pynvml מלא שקורא הספק, טמפרטורה, ניצול וזיכרון - בדיוק חמשת האטריביוטים מהטבלה בתחילת השיעור:
# gpu_stats.py install: pip install nvidia-ml-py
import pynvml as nv
nv.nvmlInit() # loads libnvml.so
try:
h = nv.nvmlDeviceGetHandleByIndex(0) # handle to card 0
name = nv.nvmlDeviceGetName(h)
if isinstance(name, bytes): # older versions return bytes
name = name.decode()
power_w = nv.nvmlDeviceGetPowerUsage(h) / 1000.0 # comes in mW
limit_w = nv.nvmlDeviceGetPowerManagementLimit(h) / 1000.0
temp_c = nv.nvmlDeviceGetTemperature(h, nv.NVML_TEMPERATURE_GPU)
util = nv.nvmlDeviceGetUtilizationRates(h) # .gpu, .memory
mem = nv.nvmlDeviceGetMemoryInfo(h) # .used, .total (in bytes)
sm_clock = nv.nvmlDeviceGetClockInfo(h, nv.NVML_CLOCK_SM)
print(f"Card: {name}")
print(f"Power: {power_w:6.1f} W / limit {limit_w:.0f} W")
print(f"Temperature: {temp_c} C")
print(f"GPU util: {util.gpu}% mem util: {util.memory}%")
print(f"Memory: {mem.used>>20} MiB / {mem.total>>20} MiB")
print(f"SM clock: {sm_clock} MHz")
finally:
nv.nvmlShutdown() # always shut down
שימו לב לפרטים החשובים: nvmlInit() טוענת את libnvml.so, כל nvmlDeviceGet... היא הפונקציה היציבה שגם nvidia-smi קורא לה, ההספק מגיע ב-מיליוואט (חילקנו ב-1000), הזיכרון ב-בתים (הזזנו 20 סיביות ל-MiB), ובסוף nvmlShutdown() בבלוק finally כדי לשחרר את הספרייה תמיד. אם נרצה לדגום לאורך זמן תוך כדי ריצת workload - בדיוק תרגיל 2 - נעטוף את גוף הקריאות בלולאה עם time.sleep(0.1) ונצבור מינימום/מקסימום/ממוצע.
לקביעת הגדרות מ-Python (למשל תקרת הספק) יש את פונקציות ה-set המקבילות, שדורשות root:
lo, hi = nv.nvmlDeviceGetPowerManagementLimitConstraints(h) # allowed range, in mW
nv.nvmlDeviceSetPowerManagementLimit(h, 500_000) # 500W, in mW, requires root
MIG וצוואר-בקבוק של הספק, תדר וחום¶
שני נושאים לסיום, ששניהם מתחברים לביצועים.
ריבוי מופעי GPU - MIG (Multi-Instance GPU). על כרטיסים מדור Ampere ומעלה (A100, H100) אפשר לחתוך GPU יחיד לכמה מופעים (instances) מבודדים חומרתית, שכל אחד מקבל פרוסה קבועה של SMs, זיכרון ו-cache. זה מאפשר להריץ כמה workloads נפרדים על אותו כרטיס פיזי בלי שיפריעו זה לזה - שימושי בענן ובשירותי inference. את MIG מפעילים דרך nvidia-smi:
sudo nvidia-smi -mig 1 # enables MIG mode (requires a reset)
sudo nvidia-smi mig -cgi 9 -C # creates an instance and a compute profile
nvidia-smi -L # shows the instances and their UUIDs
שדה ה-MIG M. בטבלה הראשית מראה אם המצב פעיל, ו-NVML חושפת את מצב ה-MIG ואת המופעים דרך ה-API שלה. זהו כלי ניהול קלאסי - הוא לא משנה איך kernel רץ, אלא כיצד הכרטיס מחולק.
הספק, תדר וחום כצוואר-בקבוק. ראינו ב-dmon איך התדר יורד כשההספק נוגע בתקרה. זה לא ניואנס - זה יכול להיות החוסם המרכזי של הביצועים. מודל ה-Roofline (שנכיר בפרק 7) מניח תפוקת שיא קבועה, אבל בפועל תפוקת השיא עצמה דינמית: היא נקבעת על ידי כמה חשמל אפשר להכניס לשבב (power ingress) וכמה חום אפשר להוציא ממנו (heat egress). kernel שמריץ הרבה FMA צפוף יגיע במהירות לתקרת ה-700W, התדר יירד מ-1980 ל-1755 MHz, וה-TFLOPS בפועל יהיה נמוך מהשיא התיאורטי - לא כי החישוב לא יעיל, אלא כי אין מספיק תקציב הספק. בפרק 7.1 נראה איך power ingress ו-heat egress קובעים תקרה פיזית שאף אופטימיזציה של קוד לא תעקוף. NVML ו-nvidia-smi הם בדיוק העיניים שמאפשרות לזהות שזה מה שקורה: אם SW Power Cap : Active וה-pclk יורד תחת עומס, אתם power-bound, לא algorithm-bound.
סיכום¶
- NVML (NVIDIA Management Library) היא ספריית הניהול והניטור של NVIDIA, ממומשת על לינוקס ב-
libnvml.so, ויושבת בשכבת ה-host software בין ה-GPU driver לכלים - היא קוראת ומשנה טלמטריה, לא משיקה kernels. - האטריביוטים המרכזיים ש-NVML חושפת: power draw, temperature, allocated memory, power limit, ו-power limiting state (האם מווסת ולמה), וכן תדרי SM וזיכרון.
nvidia-smiהוא עטיפת CLI דקה מעל NVML: כל מספר שהוא מציג וכל הגדרה שהוא משנה עוברים דרךlibnvml.so.- לתשאול:
--query-gpu=...--format=csvלשדות ממוקדים,-q -d PERFORMANCEלסיבות הוויסות, ו-dmon/pmonלטלמטריה חיה של שנייה-לשורה ברמת device ורמת תהליך. - לניהול:
-pm(מצב מתמיד),-c(מצב חישוב),-pl(תקרת הספק),-ac/-lgc/-lmc(נעילת תדרים), ו--r(איפוס); רוב הפעולות דורשות root ומצב מתמיד. - אסור לגרד את הפלט הטקסטואלי של
nvidia-smi- הוא אינו מובטח כיציב; לכלים אמיתיים משתמשים ב-C API היציב של NVML (או ב-XML-xכפשרה מובנית). - מעטפות השפה pynvml (Python) ו-nvml_wrapper (Rust) נותנות גישה ישירה ל-NVML; שימו לב שהספק מגיע ב-mW וזיכרון בבתים, ותמיד קוראים
nvmlShutdown(). - MIG (Multi-Instance GPU) חותך GPU יחיד לכמה מופעים מבודדים חומרתית, מנוהל דרך
nvidia-smi -mig, ומדווח בשדהMIG M.וב-NVML. - הספק, תדר וחום הם צוואר-בקבוק אמיתי: כשההספק נוגע בתקרה התדר יורד, תפוקת השיא בפועל נמוכה מהתיאורטית, ו-
SW Power Cap : Activeהוא הסימן לכך - קשר שנעמיק בו בפרק 7.1.