提问人:zia-khan 提问时间:11/15/2023 更新时间:11/15/2023 访问量:38
用于检索 CPU 利用率百分比的 Swift 脚本如果通过 Timer 循环运行,则工作正常。似乎在没有计时器的情况下卡在初始百分比上
Swift script to retrieve CPU utilization percentage works fine if run in a loop via Timer. Seems to get stuck on initial percentage without Timer
问:
我不是一个敏捷的开发人员。我需要这个 swift 代码来按需获取当前的 CPU 利用率百分比,即每当我查询它时,我都应该获得当前读数。我需要从 node.js 进程运行此脚本。这基本上就像从终端运行脚本一样。这是我从另一个 SO 线程中找到的脚本,它似乎可以正常工作。
import Foundation
// CPU usage credit VenoMKO: https://stackoverflow.com/a/6795612/1033581
class MyCpuUsage {
var cpuInfo: processor_info_array_t!
var prevCpuInfo: processor_info_array_t?
var numCpuInfo: mach_msg_type_number_t = 0
var numPrevCpuInfo: mach_msg_type_number_t = 0
var numCPUs: uint = 0
var updateTimer: Timer!
let CPUUsageLock: NSLock = NSLock()
init() {
let mibKeys: [Int32] = [ CTL_HW, HW_NCPU ]
// sysctl Swift usage credit Matt Gallagher: https://github.com/mattgallagher/CwlUtils/blob/master/Sources/CwlUtils/CwlSysctl.swift
mibKeys.withUnsafeBufferPointer() { mib in
var sizeOfNumCPUs: size_t = MemoryLayout<uint>.size
let status = sysctl(processor_info_array_t(mutating: mib.baseAddress), 2, &numCPUs, &sizeOfNumCPUs, nil, 0)
if status != 0 {
numCPUs = 1
}
updateTimer = Timer.scheduledTimer(timeInterval: 3, target: self, selector: #selector(updateInfo), userInfo: nil, repeats: true)
}
}
@objc func updateInfo(_ timer: Timer) {
var totalInUse: Int32 = 0
var totalTotal: Int32 = 0
var numCPUsU: natural_t = 0
let err: kern_return_t = host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &numCPUsU, &cpuInfo, &numCpuInfo);
if err == KERN_SUCCESS {
CPUUsageLock.lock()
for i in 0 ..< Int32(numCPUs) {
var inUse: Int32
var total: Int32
if let prevCpuInfo = prevCpuInfo {
inUse = cpuInfo[Int(CPU_STATE_MAX * i + CPU_STATE_USER)]
- prevCpuInfo[Int(CPU_STATE_MAX * i + CPU_STATE_USER)]
+ cpuInfo[Int(CPU_STATE_MAX * i + CPU_STATE_SYSTEM)]
- prevCpuInfo[Int(CPU_STATE_MAX * i + CPU_STATE_SYSTEM)]
+ cpuInfo[Int(CPU_STATE_MAX * i + CPU_STATE_NICE)]
- prevCpuInfo[Int(CPU_STATE_MAX * i + CPU_STATE_NICE)]
total = inUse + (cpuInfo[Int(CPU_STATE_MAX * i + CPU_STATE_IDLE)]
- prevCpuInfo[Int(CPU_STATE_MAX * i + CPU_STATE_IDLE)])
} else {
inUse = cpuInfo[Int(CPU_STATE_MAX * i + CPU_STATE_USER)]
+ cpuInfo[Int(CPU_STATE_MAX * i + CPU_STATE_SYSTEM)]
+ cpuInfo[Int(CPU_STATE_MAX * i + CPU_STATE_NICE)]
total = inUse + cpuInfo[Int(CPU_STATE_MAX * i + CPU_STATE_IDLE)]
}
totalInUse += inUse
totalTotal += total
}
CPUUsageLock.unlock()
if let prevCpuInfo = prevCpuInfo {
// vm_deallocate Swift usage credit rsfinn: https://stackoverflow.com/a/48630296/1033581
let prevCpuInfoSize: size_t = MemoryLayout<integer_t>.stride * Int(numPrevCpuInfo)
vm_deallocate(mach_task_self_, vm_address_t(bitPattern: prevCpuInfo), vm_size_t(prevCpuInfoSize))
}
prevCpuInfo = cpuInfo
numPrevCpuInfo = numCpuInfo
cpuInfo = nil
numCpuInfo = 0
let totalUsagePercentage = Float(totalInUse) / Float(totalTotal) * 100
print(String(format: "Total CPU Usage: %.2f%%", totalUsagePercentage))
} else {
print("Error!")
}
}
}
let cpuMonitor = MyCpuUsage()
RunLoop.current.run()
如您所见,此脚本使用计时器,并在控制台上每 3 秒无限期地打印一次当前百分比。由于我只想按需打印一次结果,因此以下是我所做的修改。只是删除了计时器,或者我想我做到了。
import Foundation
class MyCpuUsage {
var cpuInfo: processor_info_array_t!
var prevCpuInfo: processor_info_array_t?
var numCpuInfo: mach_msg_type_number_t = 0
var numPrevCpuInfo: mach_msg_type_number_t = 0
var numCPUs: uint = 0
let CPUUsageLock: NSLock = NSLock()
init() {
let mibKeys: [Int32] = [ CTL_HW, HW_NCPU ]
mibKeys.withUnsafeBufferPointer() { mib in
var sizeOfNumCPUs: size_t = MemoryLayout<uint>.size
let status = sysctl(processor_info_array_t(mutating: mib.baseAddress), 2, &numCPUs, &sizeOfNumCPUs, nil, 0)
if status != 0 {
numCPUs = 1
}
// Timer.scheduledTimer(timeInterval: 3, target: self, selector: #selector(updateInfo), userInfo: nil, repeats: true)
// calling the updateInfo() func to print one time result
updateInfo()
}
}
// Removed exposure to objective-c and the unused parameter "timer"
// @objc func updateInfo(_ timer: Timer) {
func updateInfo() {
// I have added these two variables to calculate sum of all cores
var totalInUse: Int32 = 0
var totalTotal: Int32 = 0
var numCPUsU: natural_t = 0
let err: kern_return_t = host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &numCPUsU, &cpuInfo, &numCpuInfo);
if err == KERN_SUCCESS {
CPUUsageLock.lock()
for i in 0 ..< Int32(numCPUs) {
var inUse: Int32
var total: Int32
if let prevCpuInfo = prevCpuInfo {
inUse = cpuInfo[Int(CPU_STATE_MAX * i + CPU_STATE_USER)]
- prevCpuInfo[Int(CPU_STATE_MAX * i + CPU_STATE_USER)]
+ cpuInfo[Int(CPU_STATE_MAX * i + CPU_STATE_SYSTEM)]
- prevCpuInfo[Int(CPU_STATE_MAX * i + CPU_STATE_SYSTEM)]
+ cpuInfo[Int(CPU_STATE_MAX * i + CPU_STATE_NICE)]
- prevCpuInfo[Int(CPU_STATE_MAX * i + CPU_STATE_NICE)]
total = inUse + (cpuInfo[Int(CPU_STATE_MAX * i + CPU_STATE_IDLE)]
- prevCpuInfo[Int(CPU_STATE_MAX * i + CPU_STATE_IDLE)])
} else {
inUse = cpuInfo[Int(CPU_STATE_MAX * i + CPU_STATE_USER)]
+ cpuInfo[Int(CPU_STATE_MAX * i + CPU_STATE_SYSTEM)]
+ cpuInfo[Int(CPU_STATE_MAX * i + CPU_STATE_NICE)]
total = inUse + cpuInfo[Int(CPU_STATE_MAX * i + CPU_STATE_IDLE)]
}
// print(String(format: "Core: %u Usage: %f", i, Float(inUse) / Float(total)))
// Assigning totals here
totalInUse += inUse
totalTotal += total
}
CPUUsageLock.unlock()
if let prevCpuInfo = prevCpuInfo {
let prevCpuInfoSize: size_t = MemoryLayout<integer_t>.stride * Int(numPrevCpuInfo)
vm_deallocate(mach_task_self_, vm_address_t(bitPattern: prevCpuInfo), vm_size_t(prevCpuInfoSize))
}
prevCpuInfo = cpuInfo
numPrevCpuInfo = numCpuInfo
cpuInfo = nil
numCpuInfo = 0
let totalUsagePercentage = Float(totalInUse) / Float(totalTotal) * 100
// This is where I print sum of all cores percentage.
print(String(format: "Total CPU Usage: %.2f%%", totalUsagePercentage))
} else {
print("Error!")
}
}
}
let cpuMonitor = MyCpuUsage()
我的修改版本的问题在于,它似乎在每次调用时打印的百分比与从终端进行第一次调用时打印的百分比相同。不仅控制台上的打印值保持不变,而且我还尝试运行进程以增加活动监视器确认的 CPU 使用率,但此脚本继续显示相同的值。任何帮助将不胜感激。
这是终端的屏幕截图。
答:
1赞
3 revsCœur
#1
原始代码(2011 年)中的公式依赖于对 的两次调用之间的用法差异。因此,要使您的想法在没有计时器的情况下工作,您仍然需要至少两次调用 .例如,您可以尝试在两者之间进行第二次。host_processor_info
updateInfo()
sleep(1)
代码注释。
在 Swift Playground 中测试。
import Foundation
class MyCpuUsage {
var cpuInfo: processor_info_array_t!
var prevCpuInfo: processor_info_array_t?
var numCpuInfo: mach_msg_type_number_t = 0
var numPrevCpuInfo: mach_msg_type_number_t = 0
let numCPUs: uint
let CPUUsageLock = NSLock()
init() {
// obtaining numCPUs
let mibKeys: [Int32] = [ CTL_HW, HW_NCPU ]
var numCPUs: uint = 0
mibKeys.withUnsafeBufferPointer() { mib in
var sizeOfNumCPUs = MemoryLayout<uint>.size
let status = sysctl(processor_info_array_t(mutating: mib.baseAddress), 2, &numCPUs, &sizeOfNumCPUs, nil, 0)
if status != 0 {
numCPUs = 1
}
}
self.numCPUs = numCPUs
// calling the updateInfo() func to initialize prevCpuInfo
updateInfo()
// sleep 1 second
sleep(1)
// calling the updateInfo() func to print cpu usage during past second
updateInfo()
}
func updateInfo() {
var numCPUsU: natural_t = 0
let err: kern_return_t = host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &numCPUsU, &cpuInfo, &numCpuInfo);
guard err == KERN_SUCCESS else {
print("Error host_processor_info!")
return
}
// Two variables to calculate sum of all cores
var totalInUse: Int32 = 0
var totalTotal: Int32 = 0
CPUUsageLock.lock()
for i in 0 ..< Int32(numCPUs) {
var inUse: Int32
var total: Int32
if let prevCpuInfo = prevCpuInfo {
inUse = cpuInfo[Int(CPU_STATE_MAX * i + CPU_STATE_USER)]
- prevCpuInfo[Int(CPU_STATE_MAX * i + CPU_STATE_USER)]
+ cpuInfo[Int(CPU_STATE_MAX * i + CPU_STATE_SYSTEM)]
- prevCpuInfo[Int(CPU_STATE_MAX * i + CPU_STATE_SYSTEM)]
+ cpuInfo[Int(CPU_STATE_MAX * i + CPU_STATE_NICE)]
- prevCpuInfo[Int(CPU_STATE_MAX * i + CPU_STATE_NICE)]
total = inUse + (cpuInfo[Int(CPU_STATE_MAX * i + CPU_STATE_IDLE)]
- prevCpuInfo[Int(CPU_STATE_MAX * i + CPU_STATE_IDLE)])
} else {
inUse = cpuInfo[Int(CPU_STATE_MAX * i + CPU_STATE_USER)]
+ cpuInfo[Int(CPU_STATE_MAX * i + CPU_STATE_SYSTEM)]
+ cpuInfo[Int(CPU_STATE_MAX * i + CPU_STATE_NICE)]
total = inUse + cpuInfo[Int(CPU_STATE_MAX * i + CPU_STATE_IDLE)]
}
// Assigning totals here
totalInUse += inUse
totalTotal += total
}
CPUUsageLock.unlock()
if let prevCpuInfo = prevCpuInfo {
// I free the memory of prevCpuInfo
let prevCpuInfoSize: size_t = MemoryLayout<integer_t>.stride * Int(numPrevCpuInfo)
vm_deallocate(mach_task_self_, vm_address_t(bitPattern: prevCpuInfo), vm_size_t(prevCpuInfoSize))
// I print sum of all cores percentage.
let totalUsagePercentage = Float(totalInUse) / Float(totalTotal) * 100
print(String(format: "Total CPU Usage: %.2f%%", totalUsagePercentage))
}
prevCpuInfo = cpuInfo
numPrevCpuInfo = numCpuInfo
cpuInfo = nil
numCpuInfo = 0
}
}
let cpuMonitor = MyCpuUsage()
作为记录,在两个电话之间等待 1 秒的想法也在 2013 年由 Anya 在类似的 ObjC 回答中记录:
然后我们取两个样本(样本之间间隔 1 秒)
sample(&sample1); sleep(1); sample(&sample2);
评论
0赞
zia-khan
11/15/2023
非常感谢:-)这很完美。在某个地方,我还发现使用了这种方法,而不是我们在这种方法中的简单方法。我发现两个显示器几乎完全相同的结果。有什么主要考虑因素吗?差异?let hostInfo = host_cpu_load_info_t.allocate(capacity: 1) let _ = hostInfo.withMemoryRebound(to: integer_t.self, capacity: Int(size)) { (pointer) -> kern_return_t in return host_statistics(mach_host_self(), HOST_CPU_LOAD_INFO, pointer, &size) }
host_processor_info
0赞
Cœur
11/15/2023
@zia-khan 两者都没有正确记录:host_statistics,host_processor_info
。
所以我不知道。尝试阅读 VenoMKO 推荐,也许答案就在那里:github.com/gopalkrishnareddy/awesome-iOS-resource/blob/master/......
0赞
Cœur
11/15/2023
@zia-khan 你也可以尝试查看这些方法的旧源代码,这些方法在几年前是 Apple 开源的,这可能有助于你理解这两个 API 之间的区别: github.com/apple/darwin-xnu/blob/main/osfmk/kern/host.c
1赞
Cœur
11/15/2023
@zia-khan 我读到 Anya 在 2013 年写道:“但老实说,我会使用 host_statistics
,因为代码更干净”
评论
updateInfo()
updateInfo()