mirror of
https://hubproxy.babadafafafafa.cn/https://github.com/1Panel-dev/1Panel.git
synced 2026-09-21 00:24:12 +08:00
165 lines
4.3 KiB
Go
165 lines
4.3 KiB
Go
package gpu
|
|
|
|
import (
|
|
"fmt"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/1Panel-dev/1Panel/agent/utils/ai_tools/gpu/common"
|
|
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
|
)
|
|
|
|
var (
|
|
ascendVersionPattern = regexp.MustCompile(`(?i)\bVersion:\s*([^\s|]+)`)
|
|
ascendMemoryPattern = regexp.MustCompile(`([0-9]+(?:\.[0-9]+)?)\s*/\s*([0-9]+(?:\.[0-9]+)?)`)
|
|
)
|
|
|
|
type AscendSMI struct{}
|
|
|
|
func (a AscendSMI) LoadGpuInfo() (*common.GpuInfo, error) {
|
|
cmdMgr := cmd.NewCommandMgr(cmd.WithTimeout(5 * time.Second))
|
|
itemData, err := cmdMgr.RunWithStdout("npu-smi", "info")
|
|
if err != nil {
|
|
return nil, fmt.Errorf("calling npu-smi failed, %v", err)
|
|
}
|
|
return parseAscendSMI(itemData), nil
|
|
}
|
|
|
|
func parseAscendSMI(data string) *common.GpuInfo {
|
|
info := &common.GpuInfo{Type: "ascend"}
|
|
if match := ascendVersionPattern.FindStringSubmatch(data); len(match) == 2 {
|
|
info.DriverVersion = match[1]
|
|
}
|
|
|
|
processSection := false
|
|
deviceIndexes := make(map[uint]int)
|
|
var pending *common.GPU
|
|
|
|
for _, line := range strings.Split(data, "\n") {
|
|
if strings.Contains(line, "Process id") && strings.Contains(line, "Process memory") {
|
|
processSection = true
|
|
pending = nil
|
|
continue
|
|
}
|
|
|
|
cells := ascendTableCells(line)
|
|
if processSection {
|
|
if len(cells) != 4 && len(cells) != 5 {
|
|
continue
|
|
}
|
|
deviceFields := strings.Fields(cells[0])
|
|
if len(deviceFields) == 0 {
|
|
continue
|
|
}
|
|
deviceID, err := strconv.ParseUint(deviceFields[0], 10, 64)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
index, ok := deviceIndexes[uint(deviceID)]
|
|
if !ok {
|
|
continue
|
|
}
|
|
processOffset := len(cells) - 3
|
|
info.GPUs[index].Processes = append(info.GPUs[index].Processes, common.Process{
|
|
Pid: strings.TrimSpace(cells[processOffset]),
|
|
Type: "NPU",
|
|
ProcessName: strings.TrimSpace(cells[processOffset+1]),
|
|
UsedMemory: ascendValueWithUnit(cells[processOffset+2], "MB"),
|
|
})
|
|
continue
|
|
}
|
|
|
|
if len(cells) != 3 {
|
|
continue
|
|
}
|
|
if pending == nil {
|
|
fields := strings.Fields(cells[0])
|
|
if len(fields) < 2 {
|
|
continue
|
|
}
|
|
deviceID, err := strconv.ParseUint(fields[0], 10, 64)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
metrics := strings.Fields(cells[2])
|
|
if len(metrics) < 2 {
|
|
continue
|
|
}
|
|
pending = &common.GPU{
|
|
Type: "ascend",
|
|
Index: uint(deviceID),
|
|
ProductName: strings.Join(fields[1:], " "),
|
|
PersistenceMode: "N/A",
|
|
DisplayActive: "N/A",
|
|
ECC: "N/A",
|
|
FanSpeed: "N/A",
|
|
Temperature: ascendValueWithUnit(metrics[1], "C"),
|
|
PerformanceState: strings.TrimSpace(cells[1]),
|
|
PowerDraw: ascendValueWithUnit(metrics[0], "W"),
|
|
MaxPowerLimit: "N/A",
|
|
ComputeMode: "N/A",
|
|
MigMode: "N/A",
|
|
}
|
|
continue
|
|
}
|
|
|
|
metrics := strings.Fields(cells[2])
|
|
if len(metrics) == 0 {
|
|
pending = nil
|
|
continue
|
|
}
|
|
pending.BusID = strings.TrimSpace(cells[1])
|
|
pending.GPUUtil = ascendValueWithUnit(metrics[0], "%")
|
|
pending.MemUsed, pending.MemTotal = ascendMemoryUsage(cells[2])
|
|
deviceIndexes[pending.Index] = len(info.GPUs)
|
|
info.GPUs = append(info.GPUs, *pending)
|
|
pending = nil
|
|
}
|
|
|
|
return info
|
|
}
|
|
|
|
func ascendTableCells(line string) []string {
|
|
line = strings.TrimSpace(line)
|
|
if !strings.HasPrefix(line, "|") || !strings.HasSuffix(line, "|") {
|
|
return nil
|
|
}
|
|
parts := strings.Split(line, "|")
|
|
if len(parts) < 3 {
|
|
return nil
|
|
}
|
|
cells := make([]string, 0, len(parts)-2)
|
|
for _, part := range parts[1 : len(parts)-1] {
|
|
cells = append(cells, strings.TrimSpace(part))
|
|
}
|
|
return cells
|
|
}
|
|
|
|
func ascendMemoryUsage(value string) (string, string) {
|
|
matches := ascendMemoryPattern.FindAllStringSubmatch(value, -1)
|
|
if len(matches) == 0 {
|
|
return "N/A", "N/A"
|
|
}
|
|
// 910B exposes both generic memory and HBM. Prefer the last memory pool
|
|
// with a non-zero capacity, which selects HBM while retaining compatibility
|
|
// with devices that only expose Memory-Usage.
|
|
selected := matches[len(matches)-1]
|
|
for i := len(matches) - 1; i >= 0; i-- {
|
|
if total, err := strconv.ParseFloat(matches[i][2], 64); err == nil && total > 0 {
|
|
selected = matches[i]
|
|
break
|
|
}
|
|
}
|
|
return selected[1] + " MB", selected[2] + " MB"
|
|
}
|
|
|
|
func ascendValueWithUnit(value, unit string) string {
|
|
value = strings.TrimSpace(value)
|
|
if value == "" || strings.EqualFold(value, "N/A") || strings.EqualFold(value, "NA") {
|
|
return "N/A"
|
|
}
|
|
return value + " " + unit
|
|
}
|