Files
kenaito-media-picker/internal/sysinfo/sysinfo.go
T
2025-09-15 21:17:57 +08:00

152 lines
3.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Package sysinfo 提供系统资源检测和 worker 数量自动计算功能。
package sysinfo
import (
"os/exec"
"runtime"
"strconv"
"strings"
)
// SystemInfo 系统资源信息
type SystemInfo struct {
CPUCores int // CPU 逻辑核心数
MemoryGB float64 // 内存大小(GB)
MemoryMB int // 内存大小(MB)
}
// GetSystemInfo 获取系统资源信息
func GetSystemInfo() SystemInfo {
info := SystemInfo{
CPUCores: runtime.NumCPU(),
}
// 获取内存信息
info.MemoryMB = getMemoryMB()
info.MemoryGB = float64(info.MemoryMB) / 1024.0
return info
}
// getMemoryMB 获取系统内存大小(MB)
func getMemoryMB() int {
switch runtime.GOOS {
case "windows":
return getMemoryWindows()
case "linux":
return getMemoryLinux()
case "darwin":
return getMemoryDarwin()
default:
return 0
}
}
// getMemoryWindows 使用 wmic 获取 Windows 内存
func getMemoryWindows() int {
cmd := exec.Command("wmic", "computersystem", "get", "totalphysicalmemory", "/value")
output, err := cmd.Output()
if err != nil {
return 0
}
// 解析输出:TotalPhysicalMemory=1234567890
for _, line := range strings.Split(string(output), "\n") {
line = strings.TrimSpace(line)
if strings.HasPrefix(line, "TotalPhysicalMemory=") {
value := strings.TrimPrefix(line, "TotalPhysicalMemory=")
if bytes, err := strconv.ParseInt(value, 10, 64); err == nil {
return int(bytes / 1024 / 1024)
}
}
}
return 0
}
// getMemoryLinux 从 /proc/meminfo 获取 Linux 内存
func getMemoryLinux() int {
cmd := exec.Command("cat", "/proc/meminfo")
output, err := cmd.Output()
if err != nil {
return 0
}
// 解析 MemTotal: 12345678 kB
for _, line := range strings.Split(string(output), "\n") {
if strings.HasPrefix(line, "MemTotal:") {
fields := strings.Fields(line)
if len(fields) >= 2 {
if kb, err := strconv.ParseInt(fields[1], 10, 64); err == nil {
return int(kb / 1024)
}
}
}
}
return 0
}
// getMemoryDarwin 使用 sysctl 获取 macOS 内存
func getMemoryDarwin() int {
cmd := exec.Command("sysctl", "-n", "hw.memsize")
output, err := cmd.Output()
if err != nil {
return 0
}
value := strings.TrimSpace(string(output))
if bytes, err := strconv.ParseInt(value, 10, 64); err == nil {
return int(bytes / 1024 / 1024)
}
return 0
}
// WorkerConfig worker 配置
type WorkerConfig struct {
GoWorkers int // Go 端 worker 数量(探测/MIME/移动)
NsfwWorkers int // NudeNet Python 推理 worker 数量
AnimeNsfwWorkers int // WD Tagger Python 推理 worker 数量
}
// CalculateWorkerConfig 根据系统资源自动计算 worker 配置
// 规则:
// - Go worker: CPU核心数 - 2(留2核给Python推理和系统)
// - NudeNet worker: 因为模型小(~300MB),可以开更多,上限为 CPU核心数
// - WD Tagger worker: 因为模型大(~1.5GB),受内存限制,每个worker约需2GB
func CalculateWorkerConfig(info SystemInfo) WorkerConfig {
config := WorkerConfig{}
// Go worker: CPU核心数 - 2,最少为1
goWorkers := info.CPUCores - 2
if goWorkers < 1 {
goWorkers = 1
}
config.GoWorkers = goWorkers
// NudeNet worker: 模型小,可以开到 CPU核心数,但留一些给系统
nsfwWorkers := info.CPUCores
if nsfwWorkers > 12 {
nsfwWorkers = 12 // 上限12,避免过度并发
}
config.NsfwWorkers = nsfwWorkers
// WD Tagger worker: 受内存限制,每个worker约需2GB
// 预留4GB给系统和其他进程
availableMemoryGB := info.MemoryGB - 4
if availableMemoryGB < 0 {
availableMemoryGB = 0
}
animeWorkers := int(availableMemoryGB / 2)
if animeWorkers < 1 {
animeWorkers = 1
}
if animeWorkers > info.CPUCores {
animeWorkers = info.CPUCores // 不超过CPU核心数
}
if animeWorkers > 12 {
animeWorkers = 12 // 上限12
}
config.AnimeNsfwWorkers = animeWorkers
return config
}