3
This commit is contained in:
Binary file not shown.
+66
-66
File diff suppressed because one or more lines are too long
@@ -8,6 +8,7 @@ import (
|
|||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"math"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
@@ -375,6 +376,11 @@ type DirectDiskMetrics struct {
|
|||||||
ReadMBs float64
|
ReadMBs float64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CacheSummary struct {
|
||||||
|
Sizes map[int]int
|
||||||
|
Counts map[int]int
|
||||||
|
}
|
||||||
|
|
||||||
type NetResult struct {
|
type NetResult struct {
|
||||||
Upload float64 `json:"upload_mbps"`
|
Upload float64 `json:"upload_mbps"`
|
||||||
Download float64 `json:"download_mbps"`
|
Download float64 `json:"download_mbps"`
|
||||||
@@ -657,22 +663,32 @@ func collectCPUInfo(ctx context.Context) CPUDetails {
|
|||||||
info.CacheL3KB = l3
|
info.CacheL3KB = l3
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if info.CacheL1KB == 0 && info.CacheL2KB == 0 && info.CacheL3KB == 0 {
|
cacheSummary := collectCacheSizes(ctx, info.Cores)
|
||||||
info.CacheL1KB, info.CacheL2KB, info.CacheL3KB = collectCacheSizes(ctx, info.Cores)
|
if info.CacheL1KB == 0 && cacheSummary.Sizes[1] > 0 {
|
||||||
|
info.CacheL1KB = cacheSummary.Sizes[1]
|
||||||
}
|
}
|
||||||
applyCpuidCacheInfo(&info)
|
if info.CacheL2KB == 0 && cacheSummary.Sizes[2] > 0 {
|
||||||
|
info.CacheL2KB = cacheSummary.Sizes[2]
|
||||||
|
}
|
||||||
|
if info.CacheL3KB == 0 && cacheSummary.Sizes[3] > 0 {
|
||||||
|
info.CacheL3KB = cacheSummary.Sizes[3]
|
||||||
|
}
|
||||||
|
applyCpuidCacheInfo(&info, cacheSummary)
|
||||||
info.Microarchitecture = detectMicroarchitecture(first.ModelName, first.Family)
|
info.Microarchitecture = detectMicroarchitecture(first.ModelName, first.Family)
|
||||||
info.BaseFreqGHz, info.MaxFreqGHz = determineCPUFreqs(ctx, first)
|
info.BaseFreqGHz, info.MaxFreqGHz = determineCPUFreqs(ctx, first)
|
||||||
return info
|
return info
|
||||||
}
|
}
|
||||||
|
|
||||||
func collectCacheSizes(ctx context.Context, physicalCores int) (l1, l2, l3 int) {
|
func collectCacheSizes(ctx context.Context, physicalCores int) CacheSummary {
|
||||||
cacheDir := "/sys/devices/system/cpu/cpu0/cache"
|
cacheDir := "/sys/devices/system/cpu/cpu0/cache"
|
||||||
entries, err := os.ReadDir(cacheDir)
|
entries, err := os.ReadDir(cacheDir)
|
||||||
if err != nil {
|
summary := CacheSummary{
|
||||||
return 0, 0, 0
|
Sizes: map[int]int{},
|
||||||
|
Counts: map[int]int{},
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return summary
|
||||||
}
|
}
|
||||||
perLevel := map[int]int{}
|
|
||||||
for _, entry := range entries {
|
for _, entry := range entries {
|
||||||
if !strings.HasPrefix(entry.Name(), "index") {
|
if !strings.HasPrefix(entry.Name(), "index") {
|
||||||
continue
|
continue
|
||||||
@@ -685,16 +701,14 @@ func collectCacheSizes(ctx context.Context, physicalCores int) (l1, l2, l3 int)
|
|||||||
sizePath := filepath.Join(cacheDir, entry.Name(), "size")
|
sizePath := filepath.Join(cacheDir, entry.Name(), "size")
|
||||||
sizeKB := parseCacheSize(readStringFromFile(sizePath))
|
sizeKB := parseCacheSize(readStringFromFile(sizePath))
|
||||||
if sizeKB > 0 {
|
if sizeKB > 0 {
|
||||||
perLevel[level] += sizeKB
|
summary.Sizes[level] += sizeKB
|
||||||
|
summary.Counts[level]++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
l1 = perLevel[1]
|
return summary
|
||||||
l2 = perLevel[2]
|
|
||||||
l3 = perLevel[3]
|
|
||||||
return l1, l2, l3
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func applyCpuidCacheInfo(info *CPUDetails) {
|
func applyCpuidCacheInfo(info *CPUDetails, summary CacheSummary) {
|
||||||
cache := cpuid.CPU.Cache
|
cache := cpuid.CPU.Cache
|
||||||
if cache.L1I < 0 && cache.L1D < 0 && cache.L2 < 0 && cache.L3 < 0 {
|
if cache.L1I < 0 && cache.L1D < 0 && cache.L2 < 0 && cache.L3 < 0 {
|
||||||
return
|
return
|
||||||
@@ -706,6 +720,9 @@ func applyCpuidCacheInfo(info *CPUDetails) {
|
|||||||
cores = 1
|
cores = 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if summary.Counts == nil {
|
||||||
|
summary.Counts = map[int]int{}
|
||||||
|
}
|
||||||
l1Bytes := 0
|
l1Bytes := 0
|
||||||
if cache.L1I > 0 {
|
if cache.L1I > 0 {
|
||||||
l1Bytes += cache.L1I
|
l1Bytes += cache.L1I
|
||||||
@@ -717,10 +734,18 @@ func applyCpuidCacheInfo(info *CPUDetails) {
|
|||||||
info.CacheL1KB = int(int64(l1Bytes) * int64(cores) / 1024)
|
info.CacheL1KB = int(int64(l1Bytes) * int64(cores) / 1024)
|
||||||
}
|
}
|
||||||
if cache.L2 > 0 && info.CacheL2KB == 0 {
|
if cache.L2 > 0 && info.CacheL2KB == 0 {
|
||||||
info.CacheL2KB = int(int64(cache.L2) * int64(cores) / 1024)
|
l2Slices := summary.Counts[2]
|
||||||
|
if l2Slices <= 0 {
|
||||||
|
l2Slices = cores
|
||||||
|
}
|
||||||
|
info.CacheL2KB = maxInt(info.CacheL2KB, int((int64(cache.L2)*int64(l2Slices))/1024))
|
||||||
}
|
}
|
||||||
if cache.L3 > 0 && info.CacheL3KB == 0 {
|
if cache.L3 > 0 && info.CacheL3KB == 0 {
|
||||||
info.CacheL3KB = int(cache.L3 / 1024)
|
l3Slices := summary.Counts[3]
|
||||||
|
if l3Slices <= 0 {
|
||||||
|
l3Slices = 1
|
||||||
|
}
|
||||||
|
info.CacheL3KB = maxInt(info.CacheL3KB, int((int64(cache.L3)*int64(l3Slices))/1024))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -797,7 +822,7 @@ func determineCPUFreqs(ctx context.Context, info cpu.InfoStat) (float64, float64
|
|||||||
max = maxSpeed / 1000
|
max = maxSpeed / 1000
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return base, max
|
return round2(base), round2(max)
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseDMIDecodeSpeed(output string, re *regexp.Regexp) float64 {
|
func parseDMIDecodeSpeed(output string, re *regexp.Regexp) float64 {
|
||||||
@@ -888,6 +913,16 @@ func collectRAMInfo(ctx context.Context) RAMDetails {
|
|||||||
}
|
}
|
||||||
|
|
||||||
slotsTotal, slotsUsed, maxCapacity, ecc, layout := collectSMBIOSMemory()
|
slotsTotal, slotsUsed, maxCapacity, ecc, layout := collectSMBIOSMemory()
|
||||||
|
if len(layout) > 0 {
|
||||||
|
correctSlots := len(layout)
|
||||||
|
if slotsTotal == 0 || slotsTotal < correctSlots || slotsTotal > correctSlots*4 {
|
||||||
|
debugf("Correction RAM slots_total=%d => %d (layout=%d)", slotsTotal, correctSlots, len(layout))
|
||||||
|
slotsTotal = correctSlots
|
||||||
|
}
|
||||||
|
if slotsUsed == 0 {
|
||||||
|
slotsUsed = len(layout)
|
||||||
|
}
|
||||||
|
}
|
||||||
if slotsTotal > 0 {
|
if slotsTotal > 0 {
|
||||||
res.SlotsTotal = slotsTotal
|
res.SlotsTotal = slotsTotal
|
||||||
}
|
}
|
||||||
@@ -901,14 +936,18 @@ func collectRAMInfo(ctx context.Context) RAMDetails {
|
|||||||
res.Layout = layout
|
res.Layout = layout
|
||||||
|
|
||||||
if res.MaxCapacityMB == 0 {
|
if res.MaxCapacityMB == 0 {
|
||||||
|
debugf("RAM fallback max capacity: SMBIOS=0 => utiliser total=%dMB", res.TotalMB)
|
||||||
res.MaxCapacityMB = res.TotalMB
|
res.MaxCapacityMB = res.TotalMB
|
||||||
}
|
}
|
||||||
if res.SlotsTotal == 0 && len(layout) > 0 {
|
if res.SlotsTotal == 0 && len(layout) > 0 {
|
||||||
|
debugf("RAM fallback slots total: SMBIOS=0 => %d modules détectés", len(layout))
|
||||||
res.SlotsTotal = len(layout)
|
res.SlotsTotal = len(layout)
|
||||||
}
|
}
|
||||||
if res.SlotsUsed == 0 && len(layout) > 0 {
|
if res.SlotsUsed == 0 && len(layout) > 0 {
|
||||||
|
debugf("RAM fallback slots used: SMBIOS=0 => %d modules détectés", len(layout))
|
||||||
res.SlotsUsed = len(layout)
|
res.SlotsUsed = len(layout)
|
||||||
}
|
}
|
||||||
|
debugf("RAM result: max=%dMB slotsTotal=%d slotsUsed=%d layout=%d modules", res.MaxCapacityMB, res.SlotsTotal, res.SlotsUsed, len(layout))
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -964,19 +1003,20 @@ func parseSMBIOSPhysicalMemoryArray(info *smbios.Info) (slotsTotal int, ecc bool
|
|||||||
if table.Type != smbiosTableTypePhysicalMemoryArray {
|
if table.Type != smbiosTableTypePhysicalMemoryArray {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if val, err := table.GetByteAt(11); err == nil && val > 0 {
|
if val, err := table.GetByteAt(13); err == nil && val > 0 {
|
||||||
|
debugf("[SMBIOS RAW] Handle=%s slots_byte=0x%X (%d)", table.Handle, val, val)
|
||||||
slotsTotal = int(val)
|
slotsTotal = int(val)
|
||||||
}
|
}
|
||||||
if val, err := table.GetByteAt(6); err == nil {
|
if val, err := table.GetByteAt(6); err == nil {
|
||||||
ecc = val != 0 && val != 3
|
ecc = val != 0 && val != 3
|
||||||
}
|
}
|
||||||
if val, err := table.GetWordAt(7); err == nil {
|
if dw, err := table.GetDWordAt(7); err == nil {
|
||||||
if val >= 0x8000 && table.Len() >= 0x10 {
|
if dw == 0x80000000 && table.Len() >= 0x10 {
|
||||||
if ext, err := table.GetDWordAt(12); err == nil && ext > 0 {
|
if ext, err := table.GetDWordAt(12); err == nil && ext > 0 {
|
||||||
maxCapacityMB = int(ext / (1024 * 1024))
|
maxCapacityMB = int(ext / (1024 * 1024))
|
||||||
}
|
}
|
||||||
} else if val > 0 {
|
} else if dw > 0 {
|
||||||
maxCapacityMB = int(val)
|
maxCapacityMB = int(dw / 1024)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
@@ -2140,12 +2180,12 @@ func runCPUBench(ctx context.Context) CPUResult {
|
|||||||
singleEvents, singleDuration := runSysbenchCPU(ctx, "1")
|
singleEvents, singleDuration := runSysbenchCPU(ctx, "1")
|
||||||
multiThreads := resolveThreadCount(ctx)
|
multiThreads := resolveThreadCount(ctx)
|
||||||
multiEvents, multiDuration := runSysbenchCPU(ctx, multiThreads)
|
multiEvents, multiDuration := runSysbenchCPU(ctx, multiThreads)
|
||||||
res.EventsPerSecSingle = singleEvents
|
res.EventsPerSecSingle = round2(singleEvents)
|
||||||
res.EventsPerSecMulti = multiEvents
|
res.EventsPerSecMulti = round2(multiEvents)
|
||||||
res.EventsPerSec = averagePositive(singleEvents, multiEvents)
|
res.EventsPerSec = round2(averagePositive(singleEvents, multiEvents))
|
||||||
res.DurationSec = singleDuration + multiDuration
|
res.DurationSec = round2(singleDuration + multiDuration)
|
||||||
res.ScoreSingle = singleEvents
|
res.ScoreSingle = res.EventsPerSecSingle
|
||||||
res.ScoreMulti = multiEvents
|
res.ScoreMulti = res.EventsPerSecMulti
|
||||||
res.Score = res.EventsPerSec
|
res.Score = res.EventsPerSec
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
@@ -2211,13 +2251,13 @@ func runMemBench(ctx context.Context) MemResult {
|
|||||||
if out, err := safeRun(ctx, "sysbench", args...); err == nil {
|
if out, err := safeRun(ctx, "sysbench", args...); err == nil {
|
||||||
if matches := regexp.MustCompile(`([\d\.]+)\s+MiB/sec`).FindStringSubmatch(out); len(matches) > 1 {
|
if matches := regexp.MustCompile(`([\d\.]+)\s+MiB/sec`).FindStringSubmatch(out); len(matches) > 1 {
|
||||||
if val, err := strconv.ParseFloat(matches[1], 64); err == nil {
|
if val, err := strconv.ParseFloat(matches[1], 64); err == nil {
|
||||||
res.Throughput = val
|
res.Throughput = round2(val)
|
||||||
res.Score = val
|
res.Score = res.Throughput
|
||||||
}
|
}
|
||||||
} else if matches := regexp.MustCompile(`transferred:\s+([\d\.]+)\s+MiB`).FindStringSubmatch(out); len(matches) > 1 {
|
} else if matches := regexp.MustCompile(`transferred:\s+([\d\.]+)\s+MiB`).FindStringSubmatch(out); len(matches) > 1 {
|
||||||
if val, err := strconv.ParseFloat(matches[1], 64); err == nil {
|
if val, err := strconv.ParseFloat(matches[1], 64); err == nil {
|
||||||
res.Throughput = val
|
res.Throughput = round2(val)
|
||||||
res.Score = val
|
res.Score = res.Throughput
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2266,7 +2306,12 @@ func runDiskBench(ctx context.Context) DiskResult {
|
|||||||
res.IOPSWrite = convertFloat(write["iops"])
|
res.IOPSWrite = convertFloat(write["iops"])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
res.Score = averagePositive(res.ReadMBs, res.WriteMBs)
|
res.Score = round2(averagePositive(res.ReadMBs, res.WriteMBs))
|
||||||
|
res.ReadMBs = round2(res.ReadMBs)
|
||||||
|
res.WriteMBs = round2(res.WriteMBs)
|
||||||
|
res.LatencyMs = round2(res.LatencyMs)
|
||||||
|
res.IOPSRead = round2(res.IOPSRead)
|
||||||
|
res.IOPSWrite = round2(res.IOPSWrite)
|
||||||
debugf("[3/6] Bench disque final -> read=%.2f MB/s write=%.2f MB/s score=%.2f", res.ReadMBs, res.WriteMBs, res.Score)
|
debugf("[3/6] Bench disque final -> read=%.2f MB/s write=%.2f MB/s score=%.2f", res.ReadMBs, res.WriteMBs, res.Score)
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
@@ -2283,6 +2328,13 @@ func convertBW(value interface{}) float64 {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func maxInt(a, b int) int {
|
||||||
|
if b > a {
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
func runDirectDiskTests(ctx context.Context, benchDir string, mountInfo mountEntry) DirectDiskMetrics {
|
func runDirectDiskTests(ctx context.Context, benchDir string, mountInfo mountEntry) DirectDiskMetrics {
|
||||||
if benchDir == "" {
|
if benchDir == "" {
|
||||||
return DirectDiskMetrics{}
|
return DirectDiskMetrics{}
|
||||||
@@ -2334,8 +2386,8 @@ func runDirectDiskTests(ctx context.Context, benchDir string, mountInfo mountEnt
|
|||||||
|
|
||||||
cleanupPaths(cleanup)
|
cleanupPaths(cleanup)
|
||||||
metrics := DirectDiskMetrics{
|
metrics := DirectDiskMetrics{
|
||||||
WriteMBs: safeThroughput(totalWriteMB, totalWriteDur),
|
WriteMBs: round2(safeThroughput(totalWriteMB, totalWriteDur)),
|
||||||
ReadMBs: safeThroughput(totalReadMB, totalReadDur),
|
ReadMBs: round2(safeThroughput(totalReadMB, totalReadDur)),
|
||||||
}
|
}
|
||||||
debugf("[3/6] Bench disque direct résumé -> write=%.2f MB/s read=%.2f MB/s", metrics.WriteMBs, metrics.ReadMBs)
|
debugf("[3/6] Bench disque direct résumé -> write=%.2f MB/s read=%.2f MB/s", metrics.WriteMBs, metrics.ReadMBs)
|
||||||
return metrics
|
return metrics
|
||||||
@@ -2430,6 +2482,13 @@ func fillBuffer(buf []byte) {
|
|||||||
buf[i] = byte(i % 256)
|
buf[i] = byte(i % 256)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func round2(v float64) float64 {
|
||||||
|
if math.IsNaN(v) || math.IsInf(v, 0) {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
return math.Round(v*100) / 100
|
||||||
|
}
|
||||||
func convertFloat(value interface{}) float64 {
|
func convertFloat(value interface{}) float64 {
|
||||||
if v, ok := value.(float64); ok {
|
if v, ok := value.(float64); ok {
|
||||||
return v
|
return v
|
||||||
@@ -2472,23 +2531,27 @@ func runNetBench(ctx context.Context) NetResult {
|
|||||||
}
|
}
|
||||||
if end, ok := iperfJSON["end"].(map[string]interface{}); ok {
|
if end, ok := iperfJSON["end"].(map[string]interface{}); ok {
|
||||||
if sumSent, ok := end["sum_sent"].(map[string]interface{}); ok {
|
if sumSent, ok := end["sum_sent"].(map[string]interface{}); ok {
|
||||||
res.Upload = convertFloat(sumSent["bits_per_second"]) / 1000 / 1000
|
if val := convertFloat(sumSent["bits_per_second"]); val > 0 {
|
||||||
|
res.Upload = round2(val / 1000 / 1000)
|
||||||
|
}
|
||||||
if val := convertFloat(sumSent["jitter_ms"]); val > 0 {
|
if val := convertFloat(sumSent["jitter_ms"]); val > 0 {
|
||||||
res.JitterMs = floatPtr(val)
|
res.JitterMs = floatPtr(round2(val))
|
||||||
}
|
}
|
||||||
if val := convertFloat(sumSent["lost_percent"]); val > 0 {
|
if val := convertFloat(sumSent["lost_percent"]); val > 0 {
|
||||||
res.PacketLossPct = floatPtr(val)
|
res.PacketLossPct = floatPtr(round2(val))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if sumRecv, ok := end["sum_received"].(map[string]interface{}); ok {
|
if sumRecv, ok := end["sum_received"].(map[string]interface{}); ok {
|
||||||
res.Download = convertFloat(sumRecv["bits_per_second"]) / 1000 / 1000
|
if val := convertFloat(sumRecv["bits_per_second"]); val > 0 {
|
||||||
|
res.Download = round2(val / 1000 / 1000)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
res.Score = averagePositive(res.Upload, res.Download)
|
res.Score = round2(averagePositive(res.Upload, res.Download))
|
||||||
// Ping test
|
// Ping test
|
||||||
if cfg.Benchmarks.Network.Server != "" {
|
if cfg.Benchmarks.Network.Server != "" {
|
||||||
if ping := measurePing(ctx, cfg.Benchmarks.Network.Server); ping > 0 {
|
if ping := measurePing(ctx, cfg.Benchmarks.Network.Server); ping > 0 {
|
||||||
res.PingMs = ping
|
res.PingMs = round2(ping)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return res
|
return res
|
||||||
|
|||||||
Reference in New Issue
Block a user