Skip to content

Commit b63aacb

Browse files
committed
improve macOS test
1 parent f5a39bd commit b63aacb

4 files changed

Lines changed: 161 additions & 91 deletions

File tree

ProcessorLatencyTool/Helpers/LatencyTester/MacOsLatencyTester.cs

Lines changed: 40 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -9,31 +9,20 @@ namespace ProcessorLatencyTool.Helpers.LatencyTester;
99
[SupportedOSPlatform("osx")]
1010
public sealed unsafe partial class MacOsLatencyTester : LatencyTesterBase
1111
{
12-
// QoS class constants from MacOS
13-
private const int QOS_CLASS_USER_INTERACTIVE = 0x21;
14-
private const int QOS_CLASS_USER_INITIATED = 0x19;
15-
private const int QOS_CLASS_DEFAULT = 0x15;
16-
private const int QOS_CLASS_UTILITY = 0x11;
17-
private const int QOS_CLASS_BACKGROUND = 0x09;
18-
19-
// Error codes
20-
private const int EPERM = 1;
21-
private const int EINVAL = 22;
22-
2312
[LibraryImport("libSystem.dylib", EntryPoint = "pthread_self")]
2413
private static partial IntPtr pthread_self();
2514

26-
[LibraryImport("libSystem.dylib", EntryPoint = "pthread_set_qos_class_self_np")]
27-
private static partial int pthread_set_qos_class_self_np(int qos_class, int relative_priority);
28-
2915
[LibraryImport("libSystem.dylib", EntryPoint = "pthread_getname_np")]
3016
private static partial int pthread_getname_np(IntPtr thread, byte* name, nuint len);
3117

3218
[LibraryImport("libSystem.dylib", EntryPoint = "pthread_setname_np")]
3319
private static partial int pthread_setname_np(byte* name);
3420

35-
[LibraryImport("libSystem.dylib", EntryPoint = "pthread_get_qos_class_np")]
36-
private static partial int pthread_get_qos_class_np(IntPtr thread, out int qos_class, out int relative_priority);
21+
[LibraryImport("arm64_registers", EntryPoint = "set_realtime_policy")]
22+
private static partial int set_realtime_policy();
23+
24+
[LibraryImport("arm64_registers", EntryPoint = "get_thread_policy")]
25+
private static partial int get_thread_policy(out int is_realtime, out int importance);
3726

3827
[LibraryImport("arm64_registers", EntryPoint = "read_tpidr_el0")]
3928
private static partial ulong ReadTpidrEl0();
@@ -44,69 +33,6 @@ public sealed unsafe partial class MacOsLatencyTester : LatencyTesterBase
4433
[LibraryImport("arm64_registers", EntryPoint = "read_cntfrq_el0")]
4534
private static partial ulong ReadCntfrqEl0();
4635

47-
private string GetQosClassName(int qosClass)
48-
{
49-
return qosClass switch
50-
{
51-
QOS_CLASS_USER_INTERACTIVE => "USER_INTERACTIVE",
52-
QOS_CLASS_USER_INITIATED => "USER_INITIATED",
53-
QOS_CLASS_DEFAULT => "DEFAULT",
54-
QOS_CLASS_UTILITY => "UTILITY",
55-
QOS_CLASS_BACKGROUND => "BACKGROUND",
56-
_ => $"UNKNOWN({qosClass})"
57-
};
58-
}
59-
60-
private bool SetQosClass(int qosClass)
61-
{
62-
var threadId = pthread_self();
63-
int currentQos;
64-
int currentPriority;
65-
66-
// Get current QoS class
67-
var getResult = pthread_get_qos_class_np(threadId, out currentQos, out currentPriority);
68-
if (getResult == 0)
69-
{
70-
Console.WriteLine($"Current QoS class: {GetQosClassName(currentQos)} (priority: {currentPriority})");
71-
}
72-
73-
var result = pthread_set_qos_class_self_np(qosClass, 0);
74-
if (result != 0)
75-
{
76-
switch (result)
77-
{
78-
case EPERM:
79-
Console.WriteLine($"Warning: Permission denied setting QoS class to {GetQosClassName(qosClass)}. Try running with sudo.");
80-
break;
81-
case EINVAL:
82-
Console.WriteLine($"Warning: Invalid QoS class value {GetQosClassName(qosClass)}");
83-
break;
84-
default:
85-
Console.WriteLine($"Warning: Failed to set QoS class to {GetQosClassName(qosClass)} (error {result})");
86-
break;
87-
}
88-
return false;
89-
}
90-
91-
// Verify the change
92-
getResult = pthread_get_qos_class_np(threadId, out currentQos, out currentPriority);
93-
if (getResult == 0)
94-
{
95-
if (currentQos == qosClass)
96-
{
97-
Console.WriteLine($"Successfully set QoS class to {GetQosClassName(currentQos)}");
98-
return true;
99-
}
100-
else
101-
{
102-
Console.WriteLine($"Warning: QoS class mismatch. Requested: {GetQosClassName(qosClass)}, Current: {GetQosClassName(currentQos)}");
103-
return false;
104-
}
105-
}
106-
107-
return true;
108-
}
109-
11036
private void SetThreadName(string name)
11137
{
11238
// Max thread name length in MacOS is 64 bytes
@@ -127,17 +53,42 @@ protected override void SetThreadAffinity(int core)
12753
// Set thread name for debugging
12854
SetThreadName($"LatencyTest_Core{core}");
12955

130-
// First try to set QoS class to user interactive
131-
if (!SetQosClass(QOS_CLASS_USER_INTERACTIVE))
56+
// Get current thread policy
57+
int isRealtime, importance;
58+
var result = get_thread_policy(out isRealtime, out importance);
59+
if (result == 0)
60+
{
61+
Console.WriteLine($"Current thread policy - Realtime: {isRealtime}, Importance: {importance}");
62+
}
63+
64+
// Try to set realtime policy
65+
result = set_realtime_policy();
66+
if (result != 0)
67+
{
68+
switch (result)
69+
{
70+
case -1:
71+
Console.WriteLine("Warning: Could not get thread port");
72+
break;
73+
case -2:
74+
Console.WriteLine("Warning: Could not set extended policy");
75+
break;
76+
case -3:
77+
Console.WriteLine("Warning: Could not set precedence policy");
78+
break;
79+
default:
80+
Console.WriteLine($"Warning: Unknown error setting thread policy ({result})");
81+
break;
82+
}
83+
Console.WriteLine("Warning: Failed to set realtime policy. Performance may be affected.");
84+
}
85+
else
13286
{
133-
// If that fails, try user initiated
134-
if (!SetQosClass(QOS_CLASS_USER_INITIATED))
87+
// Verify the change
88+
result = get_thread_policy(out isRealtime, out importance);
89+
if (result == 0)
13590
{
136-
// If that also fails, try default
137-
if (!SetQosClass(QOS_CLASS_DEFAULT))
138-
{
139-
Console.WriteLine("Warning: Failed to set any QoS class. Performance may be affected.");
140-
}
91+
Console.WriteLine($"Thread policy set - Realtime: {isRealtime}, Importance: {importance}");
14192
}
14293
}
14394

@@ -148,8 +99,6 @@ protected override void SetThreadAffinity(int core)
14899
{
149100
pthread_getname_np(threadId, namePtr, 64);
150101
}
151-
152-
Console.WriteLine($"Info: Thread {threadId} (Core {core}) set to User Interactive QoS");
153102
}
154103
catch (Exception ex)
155104
{
@@ -180,4 +129,4 @@ protected override double GetTimerPeriodNs()
180129
}
181130
return 1_000_000_000.0 / Stopwatch.Frequency;
182131
}
183-
}
132+
}

ProcessorLatencyTool/Native/CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,17 @@ if(APPLE)
1111
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_INSTALL_PREFIX})
1212
endif()
1313

14+
# Find pthread package
15+
find_package(Threads REQUIRED)
16+
1417
# Add library
1518
add_library(arm64_registers SHARED
1619
arm64_registers.c
1720
)
1821

22+
# Link against pthread
23+
target_link_libraries(arm64_registers PRIVATE Threads::Threads)
24+
1925
# Set output name
2026
if(APPLE)
2127
set_target_properties(arm64_registers PROPERTIES

ProcessorLatencyTool/Native/arm64_registers.c

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
11
#include <stdint.h>
2+
#include <pthread.h>
3+
#ifdef __APPLE__
4+
#include <mach/mach.h>
5+
#include <mach/thread_policy.h>
6+
#include <mach/thread_act.h>
7+
#endif
28

39
#ifdef __aarch64__
410

@@ -35,6 +41,105 @@ uint64_t read_cntfrq_el0(void) {
3541
return value;
3642
}
3743

44+
#ifdef __APPLE__
45+
int set_realtime_policy(void) {
46+
thread_port_t thread = mach_thread_self();
47+
if (thread == MACH_PORT_NULL) {
48+
return -1;
49+
}
50+
51+
thread_extended_policy_data_t extendedPolicy;
52+
extendedPolicy.timeshare = 0; // Set to realtime mode
53+
54+
kern_return_t kr = thread_policy_set(
55+
thread,
56+
THREAD_EXTENDED_POLICY,
57+
(thread_policy_t)&extendedPolicy,
58+
THREAD_EXTENDED_POLICY_COUNT
59+
);
60+
61+
if (kr != KERN_SUCCESS) {
62+
mach_port_deallocate(mach_task_self(), thread);
63+
return -2;
64+
}
65+
66+
// Set to maximum priority
67+
thread_precedence_policy_data_t precedencePolicy;
68+
precedencePolicy.importance = 63; // Maximum importance
69+
70+
kr = thread_policy_set(
71+
thread,
72+
THREAD_PRECEDENCE_POLICY,
73+
(thread_policy_t)&precedencePolicy,
74+
THREAD_PRECEDENCE_POLICY_COUNT
75+
);
76+
77+
mach_port_deallocate(mach_task_self(), thread);
78+
79+
if (kr != KERN_SUCCESS) {
80+
return -3;
81+
}
82+
83+
return 0;
84+
}
85+
86+
int get_thread_policy(int* is_realtime, int* importance) {
87+
thread_port_t thread = mach_thread_self();
88+
if (thread == MACH_PORT_NULL) {
89+
return -1;
90+
}
91+
92+
thread_extended_policy_data_t extendedPolicy;
93+
mach_msg_type_number_t extendedCount = THREAD_EXTENDED_POLICY_COUNT;
94+
boolean_t get_default = FALSE;
95+
96+
kern_return_t kr = thread_policy_get(
97+
thread,
98+
THREAD_EXTENDED_POLICY,
99+
(thread_policy_t)&extendedPolicy,
100+
&extendedCount,
101+
&get_default
102+
);
103+
104+
if (kr != KERN_SUCCESS) {
105+
mach_port_deallocate(mach_task_self(), thread);
106+
return -2;
107+
}
108+
109+
thread_precedence_policy_data_t precedencePolicy;
110+
mach_msg_type_number_t precedenceCount = THREAD_PRECEDENCE_POLICY_COUNT;
111+
112+
kr = thread_policy_get(
113+
thread,
114+
THREAD_PRECEDENCE_POLICY,
115+
(thread_policy_t)&precedencePolicy,
116+
&precedenceCount,
117+
&get_default
118+
);
119+
120+
mach_port_deallocate(mach_task_self(), thread);
121+
122+
if (kr != KERN_SUCCESS) {
123+
return -3;
124+
}
125+
126+
*is_realtime = extendedPolicy.timeshare ? 0 : 1;
127+
*importance = precedencePolicy.importance;
128+
129+
return 0;
130+
}
131+
#else
132+
int set_realtime_policy(void) {
133+
return -1;
134+
}
135+
136+
int get_thread_policy(int* is_realtime, int* importance) {
137+
*is_realtime = 0;
138+
*importance = 0;
139+
return -1;
140+
}
141+
#endif
142+
38143
#else
39144

40145
uint64_t read_tpidr_el0(void) {
@@ -49,4 +154,14 @@ uint64_t read_cntfrq_el0(void) {
49154
return 0;
50155
}
51156

157+
int set_realtime_policy(void) {
158+
return -1;
159+
}
160+
161+
int get_thread_policy(int* is_realtime, int* importance) {
162+
*is_realtime = 0;
163+
*importance = 0;
164+
return -1;
165+
}
166+
52167
#endif
Binary file not shown.

0 commit comments

Comments
 (0)