This commit is contained in:
2025-09-15 21:17:57 +08:00
parent 524da0830a
commit 413ac308ac
9 changed files with 394 additions and 26 deletions
+151
View File
@@ -0,0 +1,151 @@
// 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
}