Skip to content

Commit 685c7ab

Browse files
committed
feat: moved ANE utilization from read to calculation from power usage
1 parent 0c721a2 commit 685c7ab

1 file changed

Lines changed: 51 additions & 34 deletions

File tree

Modules/GPU/reader.swift

Lines changed: 51 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,32 @@ let vendors: [Data: String] = [
2424
Data.init([0x02, 0x10, 0x00, 0x00]): "AMD"
2525
]
2626

27+
private func maxANEPower(for platform: Platform?) -> Double {
28+
switch platform {
29+
case .m1, .m1Pro, .m1Max: return 2.0
30+
case .m1Ultra: return 4.0
31+
case .m2, .m2Pro, .m2Max: return 2.5
32+
case .m2Ultra: return 5.0
33+
case .m3, .m3Pro, .m3Max: return 3.0
34+
case .m3Ultra: return 6.0
35+
case .m4, .m4Pro, .m4Max: return 6.0
36+
case .m4Ultra: return 12.0
37+
case .m5, .m5Pro, .m5Max: return 8.0
38+
case .m5Ultra: return 16.0
39+
default: return 8.0
40+
}
41+
}
42+
2743
internal class InfoReader: Reader<GPUs> {
2844
private var gpus: GPUs = GPUs()
2945
private var displays: [gpu_s] = []
3046
private var devices: [device] = []
3147

3248
private var aneChannels: CFMutableDictionary? = nil
3349
private var aneSubscription: IOReportSubscriptionRef? = nil
34-
private var previousANEResidencies: [(on: Int64, total: Int64)] = []
50+
private var previousANEEnergy: Double = 0
51+
private var previousANERead: Date? = nil
52+
private var aneMaxPower: Double = 8.0
3553

3654
private var framesChannels: CFMutableDictionary? = nil
3755
private var framesSubscription: IOReportSubscriptionRef? = nil
@@ -49,6 +67,7 @@ internal class InfoReader: Reader<GPUs> {
4967
let devices = PCIdevices.filter{ $0.object(forKey: "IOName") as? String == "display" }
5068

5169
#if arch(arm64)
70+
self.aneMaxPower = maxANEPower(for: SystemKit.shared.device.platform)
5271
self.setupANE()
5372
self.setupFrames()
5473
#endif
@@ -220,10 +239,11 @@ internal class InfoReader: Reader<GPUs> {
220239
}
221240

222241
#if arch(arm64)
223-
let aneValue = self.readANEUtilization()
242+
let anePower = self.readANEPower()
243+
let aneUtil = anePower.map { min(1.0, max(0.0, $0 / self.aneMaxPower)) }
224244
let fpsValue = self.readFrames()
225245
for i in self.gpus.list.indices where self.gpus.list[i].IOClass.lowercased().contains("agx") {
226-
self.gpus.list[i].aneUtilization = aneValue ?? 0
246+
self.gpus.list[i].aneUtilization = aneUtil ?? 0
227247
self.gpus.list[i].fps = fpsValue
228248
}
229249
#endif
@@ -288,10 +308,10 @@ internal class InfoReader: Reader<GPUs> {
288308
return Double(delta) / elapsed
289309
}
290310

291-
// MARK: - ANE utilization
311+
// MARK: - ANE power
292312

293313
private func setupANE() {
294-
guard let channel = IOReportCopyChannelsInGroup("SoC Stats" as CFString, "Cluster Power States" as CFString, 0, 0, 0)?.takeRetainedValue() else { return }
314+
guard let channel = IOReportCopyChannelsInGroup("Energy Model" as CFString, nil, 0, 0, 0)?.takeRetainedValue() else { return }
295315

296316
let size = CFDictionaryGetCount(channel)
297317
guard let mutable = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, size, channel),
@@ -303,7 +323,7 @@ internal class InfoReader: Reader<GPUs> {
303323
sub?.release()
304324
}
305325

306-
private func readANEUtilization() -> Double? {
326+
private func readANEPower() -> Double? {
307327
guard let subscription = self.aneSubscription,
308328
let channels = self.aneChannels,
309329
let reportSample = IOReportCreateSamples(subscription, channels, nil)?.takeRetainedValue(),
@@ -312,48 +332,45 @@ internal class InfoReader: Reader<GPUs> {
312332
}
313333
let items = dict["IOReportChannels"] as! CFArray
314334

315-
var currentResidencies: [(on: Int64, total: Int64)] = []
335+
var currentEnergy: Double = 0
336+
var found = false
316337

317338
for i in 0..<CFArrayGetCount(items) {
318339
let item = unsafeBitCast(CFArrayGetValueAtIndex(items, i), to: CFDictionary.self)
319340

320341
guard let group = IOReportChannelGetGroup(item)?.takeUnretainedValue() as? String,
321-
group == "SoC Stats",
322-
let subgroup = IOReportChannelGetSubGroup(item)?.takeUnretainedValue() as? String,
323-
subgroup == "Cluster Power States",
342+
group == "Energy Model",
324343
let channel = IOReportChannelGetChannelName(item)?.takeUnretainedValue() as? String,
325-
channel.hasPrefix("ANE") else { continue }
344+
channel.starts(with: "ANE") else { continue }
326345

327-
let stateCount = IOReportStateGetCount(item)
328-
guard stateCount == 2 else { continue }
346+
let raw = Double(IOReportSimpleGetIntegerValue(item, 0))
347+
let unit = (IOReportChannelGetUnitLabel(item)?.takeUnretainedValue() as? String)?
348+
.trimmingCharacters(in: .whitespaces) ?? ""
329349

330-
var on: Int64 = 0
331-
var total: Int64 = 0
332-
for s in 0..<stateCount {
333-
let residency = IOReportStateGetResidency(item, s)
334-
let name = IOReportStateGetNameForIndex(item, s)?.takeUnretainedValue() as? String ?? ""
335-
total += residency
336-
if name != "INACT" {
337-
on += residency
338-
}
350+
let joules: Double
351+
switch unit.lowercased() {
352+
case "mj": joules = raw / 1e3
353+
case "uj", "µj": joules = raw / 1e6
354+
case "nj": joules = raw / 1e9
355+
case "pj": joules = raw / 1e12
356+
default: joules = raw / 1e9
339357
}
340358

341-
currentResidencies.append((on: on, total: total))
359+
currentEnergy += joules
360+
found = true
342361
}
343362

344-
guard !currentResidencies.isEmpty else { return nil }
345-
346-
defer { self.previousANEResidencies = currentResidencies }
347-
guard self.previousANEResidencies.count == currentResidencies.count else { return nil }
363+
guard found else { return nil }
348364

349-
var totalDeltaOn: Int64 = 0
350-
var totalDeltaAll: Int64 = 0
351-
for i in 0..<currentResidencies.count {
352-
totalDeltaOn += currentResidencies[i].on - self.previousANEResidencies[i].on
353-
totalDeltaAll += currentResidencies[i].total - self.previousANEResidencies[i].total
365+
let now = Date()
366+
defer {
367+
self.previousANEEnergy = currentEnergy
368+
self.previousANERead = now
354369
}
355370

356-
guard totalDeltaAll > 0 else { return 0 }
357-
return Double(totalDeltaOn) / Double(totalDeltaAll)
371+
guard let previousRead = self.previousANERead else { return 0 }
372+
let elapsed = now.timeIntervalSince(previousRead)
373+
guard elapsed > 0 else { return 0 }
374+
return (currentEnergy - self.previousANEEnergy) / elapsed
358375
}
359376
}

0 commit comments

Comments
 (0)