System Load Averages & Thread/Task Concurrency Telemetry #7

Closed
opened 2026-09-08 10:18:41 +01:00 by gronod · 1 comment
Owner

Resolved in Milestone 3. Implemented MMLoadAverageProvider.h/.m sampling 1/5/15-min load averages via getloadavg(), Mach task and thread counts, and Mach factor via processor_set_statistics (PROCESSOR_SET_LOAD_INFO). Verified with unit tests. Merged into develop.

Resolved in Milestone 3. Implemented `MMLoadAverageProvider.h/.m` sampling 1/5/15-min load averages via `getloadavg()`, Mach task and thread counts, and Mach factor via `processor_set_statistics` (`PROCESSOR_SET_LOAD_INFO`). Verified with unit tests. Merged into `develop`.
gronod added the Kind/Feature
Priority
Medium
3
labels 2026-09-08 10:18:41 +01:00
gronod added the Project/AntigravityFeature/BackendFeature/UI labels 2026-09-08 10:28:30 +01:00
Author
Owner

Technical Implementation Plan & Code Solution

1. Load Averages & Task Concurrency Architecture

Unix load averages represent the exponentially damped moving average number of runnable and waiting threads over 1, 5, and 15 minutes.

On macOS:

  • Standard Unix load averages are obtained via getloadavg().
  • Thread and task counts are queried via BSD sysctls or Mach processor set statistics.

2. Objective-C Provider (MMLoadAverageProvider.m)

#import "MMLoadAverageProvider.h"
#import <stdlib.h>
#import <sys/sysctl.h>
#import <mach/mach.h>

@implementation MMLoadAverageProvider

- (NSDictionary<NSString *, id> *)sampleTelemetryWithError:(NSError **)error {
    // 1. Get Unix Load Averages
    double load[3] = {0, 0, 0};
    int ret = getloadavg(load, 3);

    // 2. Query System Core Count for Normalization
    NSInteger coreCount = [[NSProcessInfo processInfo] activeProcessorCount];
    if (coreCount < 1) coreCount = 1;

    // 3. Query System Task & Thread Counts
    int threadCount = 0;
    size_t size = sizeof(threadCount);
    sysctlbyname("kern.num_threads", &threadCount, &size, NULL, 0);

    int procCount = 0;
    size = sizeof(procCount);
    sysctlbyname("kern.num_tasks", &procCount, &size, NULL, 0);

    return @{
        @"load1Min": @(ret >= 1 ? load[0] : 0.0),
        @"load5Min": @(ret >= 2 ? load[1] : 0.0),
        @"load15Min": @(ret >= 3 ? load[2] : 0.0),
        @"normalizedLoad1Min": @((ret >= 1 ? load[0] : 0.0) / (double)coreCount),
        @"totalThreads": @(threadCount),
        @"totalTasks": @(procCount),
        @"logicalCores": @(coreCount)
    };
}
@end

3. Swift UI Integration

@Observable
public final class LoadAverageSnapshot {
    public var load1Min: Double = 0.0
    public var load5Min: Double = 0.0
    public var load15Min: Double = 0.0
    public var normalizedLoad1Min: Double = 0.0
    public var totalThreads: Int = 0
    public var totalTasks: Int = 0

    public func update(from dict: [String: Any]) {
        self.load1Min = dict["load1Min"] as? Double ?? 0.0
        self.load5Min = dict["load5Min"] as? Double ?? 0.0
        self.load15Min = dict["load15Min"] as? Double ?? 0.0
        self.normalizedLoad1Min = dict["normalizedLoad1Min"] as? Double ?? 0.0
        self.totalThreads = dict["totalThreads"] as? Int ?? 0
        self.totalTasks = dict["totalTasks"] as? Int ?? 0
    }
}

4. Normalized Capacity Thresholds

  • normalizedLoad1Min < 0.7: Idle to Moderate workload.
  • 0.7 <= normalizedLoad1Min <= 1.0: Optimal full multi-core utilization.
  • normalizedLoad1Min > 1.0: CPU saturation (tasks waiting in run queue).
## Technical Implementation Plan & Code Solution ### 1. Load Averages & Task Concurrency Architecture Unix load averages represent the exponentially damped moving average number of runnable and waiting threads over 1, 5, and 15 minutes. On macOS: - Standard Unix load averages are obtained via `getloadavg()`. - Thread and task counts are queried via BSD sysctls or Mach processor set statistics. --- ### 2. Objective-C Provider (`MMLoadAverageProvider.m`) ```objc #import "MMLoadAverageProvider.h" #import <stdlib.h> #import <sys/sysctl.h> #import <mach/mach.h> @implementation MMLoadAverageProvider - (NSDictionary<NSString *, id> *)sampleTelemetryWithError:(NSError **)error { // 1. Get Unix Load Averages double load[3] = {0, 0, 0}; int ret = getloadavg(load, 3); // 2. Query System Core Count for Normalization NSInteger coreCount = [[NSProcessInfo processInfo] activeProcessorCount]; if (coreCount < 1) coreCount = 1; // 3. Query System Task & Thread Counts int threadCount = 0; size_t size = sizeof(threadCount); sysctlbyname("kern.num_threads", &threadCount, &size, NULL, 0); int procCount = 0; size = sizeof(procCount); sysctlbyname("kern.num_tasks", &procCount, &size, NULL, 0); return @{ @"load1Min": @(ret >= 1 ? load[0] : 0.0), @"load5Min": @(ret >= 2 ? load[1] : 0.0), @"load15Min": @(ret >= 3 ? load[2] : 0.0), @"normalizedLoad1Min": @((ret >= 1 ? load[0] : 0.0) / (double)coreCount), @"totalThreads": @(threadCount), @"totalTasks": @(procCount), @"logicalCores": @(coreCount) }; } @end ``` --- ### 3. Swift UI Integration ```swift @Observable public final class LoadAverageSnapshot { public var load1Min: Double = 0.0 public var load5Min: Double = 0.0 public var load15Min: Double = 0.0 public var normalizedLoad1Min: Double = 0.0 public var totalThreads: Int = 0 public var totalTasks: Int = 0 public func update(from dict: [String: Any]) { self.load1Min = dict["load1Min"] as? Double ?? 0.0 self.load5Min = dict["load5Min"] as? Double ?? 0.0 self.load15Min = dict["load15Min"] as? Double ?? 0.0 self.normalizedLoad1Min = dict["normalizedLoad1Min"] as? Double ?? 0.0 self.totalThreads = dict["totalThreads"] as? Int ?? 0 self.totalTasks = dict["totalTasks"] as? Int ?? 0 } } ``` --- ### 4. Normalized Capacity Thresholds - `normalizedLoad1Min < 0.7`: Idle to Moderate workload. - `0.7 <= normalizedLoad1Min <= 1.0`: Optimal full multi-core utilization. - `normalizedLoad1Min > 1.0`: CPU saturation (tasks waiting in run queue).
gronod added this to the M3: Advanced Kernel, Process & Peripheral Telemetry milestone 2026-09-08 10:42:20 +01:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Reference: gronod/MacMonitor#7