chore(detection): detect GPU vendor from files present in the system (#7908)
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
This commit is contained in:
committed by
GitHub
parent
cfc2225fc7
commit
ffb2dc4666
@@ -8,7 +8,6 @@ import (
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/jaypipes/ghw/pkg/gpu"
|
||||
"github.com/mudler/xlog"
|
||||
)
|
||||
|
||||
@@ -19,8 +18,9 @@ const (
|
||||
metal = "metal"
|
||||
nvidia = "nvidia"
|
||||
|
||||
amd = "amd"
|
||||
intel = "intel"
|
||||
amd = "amd"
|
||||
intel = "intel"
|
||||
vulkan = "vulkan"
|
||||
|
||||
nvidiaCuda13 = "nvidia-cuda-13"
|
||||
nvidiaCuda12 = "nvidia-cuda-12"
|
||||
@@ -131,26 +131,6 @@ func (s *SystemState) getSystemCapabilities() string {
|
||||
return s.GPUVendor
|
||||
}
|
||||
|
||||
func detectGPUVendor(gpus []*gpu.GraphicsCard) (string, error) {
|
||||
for _, gpu := range gpus {
|
||||
if gpu.DeviceInfo != nil {
|
||||
if gpu.DeviceInfo.Vendor != nil {
|
||||
gpuVendorName := strings.ToUpper(gpu.DeviceInfo.Vendor.Name)
|
||||
if strings.Contains(gpuVendorName, strings.ToUpper(nvidia)) {
|
||||
return nvidia, nil
|
||||
}
|
||||
if strings.Contains(gpuVendorName, strings.ToUpper(amd)) {
|
||||
return amd, nil
|
||||
}
|
||||
if strings.Contains(gpuVendorName, strings.ToUpper(intel)) {
|
||||
return intel, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// BackendPreferenceTokens returns a list of substrings that represent the preferred
|
||||
// backend implementation order for the current system capability. Callers can use
|
||||
@@ -169,6 +149,8 @@ func (s *SystemState) BackendPreferenceTokens() []string {
|
||||
return []string{"metal", "cpu"}
|
||||
case strings.HasPrefix(capStr, darwinX86):
|
||||
return []string{"darwin-x86", "cpu"}
|
||||
case strings.HasPrefix(capStr, vulkan):
|
||||
return []string{"vulkan", "cpu"}
|
||||
default:
|
||||
return []string{"cpu"}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"github.com/jaypipes/ghw/pkg/gpu"
|
||||
"github.com/mudler/LocalAI/pkg/xsysinfo"
|
||||
"github.com/mudler/xlog"
|
||||
)
|
||||
@@ -19,7 +18,6 @@ type SystemState struct {
|
||||
GPUVendor string
|
||||
Backend Backend
|
||||
Model Model
|
||||
gpus []*gpu.GraphicsCard
|
||||
VRAM uint64
|
||||
}
|
||||
|
||||
@@ -50,9 +48,7 @@ func GetSystemState(opts ...SystemStateOptions) (*SystemState, error) {
|
||||
}
|
||||
|
||||
// Detection is best-effort here, we don't want to fail if it fails
|
||||
state.gpus, _ = xsysinfo.GPUs()
|
||||
xlog.Debug("GPUs", "gpus", state.gpus)
|
||||
state.GPUVendor, _ = detectGPUVendor(state.gpus)
|
||||
state.GPUVendor, _ = xsysinfo.DetectGPUVendor()
|
||||
xlog.Debug("GPU vendor", "gpuVendor", state.GPUVendor)
|
||||
state.VRAM, _ = xsysinfo.TotalAvailableVRAM()
|
||||
xlog.Debug("Total available VRAM", "vram", state.VRAM)
|
||||
|
||||
@@ -89,21 +89,39 @@ func GPUs() ([]*gpu.GraphicsCard, error) {
|
||||
}
|
||||
|
||||
func TotalAvailableVRAM() (uint64, error) {
|
||||
// First, try ghw library detection
|
||||
gpus, err := GPUs()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
var totalVRAM uint64
|
||||
for _, gpu := range gpus {
|
||||
if gpu != nil && gpu.Node != nil && gpu.Node.Memory != nil {
|
||||
if gpu.Node.Memory.TotalUsableBytes > 0 {
|
||||
totalVRAM += uint64(gpu.Node.Memory.TotalUsableBytes)
|
||||
if err == nil {
|
||||
var totalVRAM uint64
|
||||
for _, gpu := range gpus {
|
||||
if gpu != nil && gpu.Node != nil && gpu.Node.Memory != nil {
|
||||
if gpu.Node.Memory.TotalUsableBytes > 0 {
|
||||
totalVRAM += uint64(gpu.Node.Memory.TotalUsableBytes)
|
||||
}
|
||||
}
|
||||
}
|
||||
// If we got valid VRAM from ghw, return it
|
||||
if totalVRAM > 0 {
|
||||
return totalVRAM, nil
|
||||
}
|
||||
}
|
||||
|
||||
return totalVRAM, nil
|
||||
// Fallback to binary-based detection via GetGPUMemoryUsage()
|
||||
// This works even when ghw dependencies are missing from the base image
|
||||
gpuMemoryInfo := GetGPUMemoryUsage()
|
||||
if len(gpuMemoryInfo) > 0 {
|
||||
var totalVRAM uint64
|
||||
for _, gpu := range gpuMemoryInfo {
|
||||
totalVRAM += gpu.TotalVRAM
|
||||
}
|
||||
if totalVRAM > 0 {
|
||||
xlog.Debug("VRAM detected via binary tools", "total_vram", totalVRAM)
|
||||
return totalVRAM, nil
|
||||
}
|
||||
}
|
||||
|
||||
// No VRAM detected
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func HasGPU(vendor string) bool {
|
||||
@@ -122,6 +140,66 @@ func HasGPU(vendor string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// DetectGPUVendor detects the GPU vendor using multiple methods with fallbacks.
|
||||
// First tries ghw library, then falls back to binary detection.
|
||||
// Returns vendor string (VendorNVIDIA, VendorAMD, VendorIntel, VendorVulkan) or empty string if not detected.
|
||||
// Priority order: NVIDIA > AMD > Intel > Vulkan
|
||||
func DetectGPUVendor() (string, error) {
|
||||
// First, try ghw library detection
|
||||
gpus, err := GPUs()
|
||||
if err == nil && len(gpus) > 0 {
|
||||
for _, gpu := range gpus {
|
||||
if gpu.DeviceInfo != nil && gpu.DeviceInfo.Vendor != nil {
|
||||
vendorName := strings.ToUpper(gpu.DeviceInfo.Vendor.Name)
|
||||
if strings.Contains(vendorName, strings.ToUpper(VendorNVIDIA)) {
|
||||
xlog.Debug("GPU vendor detected via ghw", "vendor", VendorNVIDIA)
|
||||
return VendorNVIDIA, nil
|
||||
}
|
||||
if strings.Contains(vendorName, strings.ToUpper(VendorAMD)) {
|
||||
xlog.Debug("GPU vendor detected via ghw", "vendor", VendorAMD)
|
||||
return VendorAMD, nil
|
||||
}
|
||||
if strings.Contains(vendorName, strings.ToUpper(VendorIntel)) {
|
||||
xlog.Debug("GPU vendor detected via ghw", "vendor", VendorIntel)
|
||||
return VendorIntel, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to binary detection (priority: NVIDIA > AMD > Intel > Vulkan)
|
||||
// Check for nvidia-smi
|
||||
if _, err := exec.LookPath("nvidia-smi"); err == nil {
|
||||
xlog.Debug("GPU vendor detected via binary", "vendor", VendorNVIDIA, "binary", "nvidia-smi")
|
||||
return VendorNVIDIA, nil
|
||||
}
|
||||
|
||||
// Check for rocm-smi (AMD)
|
||||
if _, err := exec.LookPath("rocm-smi"); err == nil {
|
||||
xlog.Debug("GPU vendor detected via binary", "vendor", VendorAMD, "binary", "rocm-smi")
|
||||
return VendorAMD, nil
|
||||
}
|
||||
|
||||
// Check for xpu-smi or intel_gpu_top (Intel)
|
||||
if _, err := exec.LookPath("xpu-smi"); err == nil {
|
||||
xlog.Debug("GPU vendor detected via binary", "vendor", VendorIntel, "binary", "xpu-smi")
|
||||
return VendorIntel, nil
|
||||
}
|
||||
if _, err := exec.LookPath("intel_gpu_top"); err == nil {
|
||||
xlog.Debug("GPU vendor detected via binary", "vendor", VendorIntel, "binary", "intel_gpu_top")
|
||||
return VendorIntel, nil
|
||||
}
|
||||
|
||||
// Check for vulkaninfo (Vulkan - lowest priority as it can detect any GPU)
|
||||
if _, err := exec.LookPath("vulkaninfo"); err == nil {
|
||||
xlog.Debug("GPU vendor detected via binary", "vendor", VendorVulkan, "binary", "vulkaninfo")
|
||||
return VendorVulkan, nil
|
||||
}
|
||||
|
||||
// No vendor detected
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// isUnifiedMemoryDevice checks if the given GPU name matches any known unified memory device
|
||||
func isUnifiedMemoryDevice(gpuName string) bool {
|
||||
gpuNameUpper := strings.ToUpper(gpuName)
|
||||
|
||||
Reference in New Issue
Block a user