我们偶然发现了Quartz Events的性能问题,尤其是CGEventPost:在GPU负载过重的情况下,CGEventPost可能会阻塞。我们创建了a small benchmark application to demonstrate the issue。该应用程序只是一个创建,发布和释放事件的循环。
您可以在下面看到运行该应用程序的结果。第一次运行是在空闲系统上。第二轮是使用FurMark(GPU压力测试),同时将表盘尽可能拉大。
18:58:01.683 EventPerformance[4946:707] Measurements: (outer should be close to 10)
18:58:01.684 EventPerformance[4946:707] inner (ms): 0.04, outer (ms): 11.02, CGEventPost (ms): 0.03
18:58:01.684 EventPerformance[4946:707] inner (ms): 0.04, outer (ms): 11.02, CGEventPost (ms): 0.03
18:58:01.685 EventPerformance[4946:707] inner (ms): 0.07, outer (ms): 10.26, CGEventPost (ms): 0.03
18:58:01.685 EventPerformance[4946:707] inner (ms): 0.06, outer (ms): 10.85, CGEventPost (ms): 0.05
18:58:01.686 EventPerformance[4946:707] inner (ms): 0.07, outer (ms): 10.41, CGEventPost (ms): 0.04
18:58:01.686 EventPerformance[4946:707] inner (ms): 0.04, outer (ms): 10.39, CGEventPost (ms): 0.03
18:58:01.686 EventPerformance[4946:707] inner (ms): 0.05, outer (ms): 11.02, CGEventPost (ms): 0.03
18:58:01.687 EventPerformance[4946:707] inner (ms): 0.03, outer (ms): 10.67, CGEventPost (ms): 0.03
18:58:01.687 EventPerformance[4946:707] inner (ms): 0.08, outer (ms): 10.09, CGEventPost (ms): 0.05
18:58:01.688 EventPerformance[4946:707] Averages: (outer should be close to 10)
18:58:01.688 EventPerformance[4946:707] avg inner (ms): 0.05, avg outer (ms): 10.64, avg post (ms): 0.03
在这里,我们可以看到发布事件平均大约需要0.03毫秒。同样,线程似乎在0.5ms左右被唤醒为时已晚。 CGEventPost中没有峰值。
19:02:02.150 EventPerformance[5241:707] Measurements: (outer should be close to 10)
19:02:02.151 EventPerformance[5241:707] inner (ms): 0.03, outer (ms): 10.23, CGEventPost (ms): 0.02
19:02:02.151 EventPerformance[5241:707] inner (ms): 0.02, outer (ms): 10.54, CGEventPost (ms): 0.02
19:02:02.151 EventPerformance[5241:707] inner (ms): 0.02, outer (ms): 11.01, CGEventPost (ms): 0.01
19:02:02.152 EventPerformance[5241:707] inner (ms): 0.02, outer (ms): 10.74, CGEventPost (ms): 0.01
19:02:02.152 EventPerformance[5241:707] inner (ms): 0.02, outer (ms): 10.20, CGEventPost (ms): 0.01
19:02:02.152 EventPerformance[5241:707] inner (ms): 10.35, outer (ms): 11.01, CGEventPost (ms): 10.35
19:02:02.152 EventPerformance[5241:707] inner (ms): 0.03, outer (ms): 10.02, CGEventPost (ms): 0.02
19:02:02.153 EventPerformance[5241:707] inner (ms): 58.90, outer (ms): 10.11, CGEventPost (ms): 58.90
19:02:02.153 EventPerformance[5241:707] inner (ms): 0.03, outer (ms): 10.12, CGEventPost (ms): 0.02
19:02:02.153 EventPerformance[5241:707] Averages: (outer should be close to 10)
19:02:02.371 EventPerformance[5241:707] avg inner (ms): 7.71, avg outer (ms): 10.44, avg post (ms): 7.71
当系统承受大量GPU负载时,发布事件可能需要花费(毫秒)毫秒(而不是微秒)。在极端GPU压力(
任何想法表示赞赏。
最佳答案
我想你是在填满队列(底层的马赫端口)...
您可以使用Instruments中的“计划”或“系统调用”工具来确认这一点。 (创建一个新的空白文档,添加工具,然后在File > Record Options...
下确保选中“延迟模式”。)这将显示您应用中的所有线程 Activity (线程阻塞,何时休眠,何时激活以及原因) 。
我首先尝试提高调用man 3 PTHREAD_SCHEDPARAM
的线程的线程优先级(参见CGEventPost
)。如果您的线程在较低优先级的线程上被阻塞,则内核应暂时提高阻塞线程的优先级,以避免优先级倒置并帮助您的任务更早完成。
总的来说,我认为您将必须实现 2线程解决方案,如下所示:
为要发布的事件创建队列。从您的主线程(或事件发布线程)将事件发布到此队列,然后发信号通知第二个线程(您创建的事件使用者线程)以遍历该队列并使用CGEventPost
发布任何未完成的事件。
当CGEventPost
阻塞时,您的第二个事件发布线程将阻塞,但这不会阻塞任何其他线程。当CGEventPost
最终解除阻止时,它将使用您的事件使用者线程发布的所有未完成事件,并且该事件使用者线程可以恢复发布事件。
另一种可能性:您可以合并事件吗?您可以将某些事件类型(鼠标移动?)合并为更少的事件。有时您可能仍然会遇到CGEventPost
的队列限制,我认为2线程方法可能是您最好的选择。