如何加速处理5000个独立任务的Mac应用程序?

时间:2013-08-15 02:37:34

标签: objective-c xcode macos parallel-processing grand-central-dispatch

我有一个长期运行(5-10小时)的Mac应用程序,可以处理5000个项目。通过执行多个转换(使用Saxon),运行一堆脚本(在Python和Racket中),收集数据,并将其序列化为一组XML文件,SQLite数据库和CoreData数据库来处理每个项目。每个项目完全独立于其他项目。

总之,它做了很多,需要很长时间,并且看起来可以高度并行化。

在加载了需要处理它的所有项目后,应用程序使用GCD并行化工作,使用dispatch_apply

dispatch_apply(numberOfItems, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^(size_t i) {
    @autoreleasepool {
        ...
    }
});

我正在使用12核(24虚拟)的Mac Pro上运行该应用程序。所以我希望一直处理24件物品。但是,我通过记录发现正在处理的项目数量在8到24之间变化。这实际上是为运行时间增加了几个小时(假设可以一次只能处理24个项目)。

一方面,也许GCD真的非常聪明,它已经给了我最大的吞吐量。但我很担心,因为大部分工作都是在这个应用程序产生的脚本中发生的,也许GCD是从不完整的信息推断出来的,并没有做出最好的决定。

任何想法如何提高性能?正确性之后,第一个所需属性缩短了此应用程序运行所需的时间。我不关心功耗,占用Mac Pro或其他任何东西。

更新:事实上,这在docs中看起来很惊人:“在任何给定时刻,并发队列执行的实际任务数量都是可变的,并且可以随着条件的变化而动态变化您的应用程序发生了变化。许多因素会影响并发队列执行的任务数量,包括可用内核数量,其他进程正在完成的工作量,以及其他任务的数量和优先级串行调度队列。“ (重点补充)看起来让其他进程正常工作会对应用程序中的计划产生负面影响。

能够只是说“同时运行这些块,每个核心一个,不要尝试更聪明地做任何事情”,这真是太好了。

1 个答案:

答案 0 :(得分:6)

如果您受到约束并确定,则可以使用NSThread API显式生成24个线程,并使每个线程从同步的工作项队列中拉出。我敢打赌,性能会明显变差。

当提交给它的工作项永远不会阻止时,GCD的效率最高。也就是说,您所描述的工作量相当复杂,并且充斥着线程阻塞的机会。对于初学者来说,你正在产生一堆其他过程。就在这里,这意味着您已经依靠操作系统来分配主任务和这些从属任务之间的时间/资源。除了设置每个子进程的OS优先级之外,OS调度程序无法知道哪些进程比其他进程更重要,并且默认情况下,您的子进程将具有与其父进程相同的优先级。也就是说,通过调整流程优先级听起来并不是什么好事。我假设您正在阻止等待从任务完成的主任务线程。这有效地停止了这个线程 - 它没有任何有用的工作。但就像我说的那样,我不认为通过调整奴隶任务的操作系统优先级可以获得很多东西,因为这听起来像是一个I / O绑定的工作流程......

接着描述三个I / O繁重的操作(“将其序列化为一组XML文件,一个SQLite数据库和一个CoreData数据库。”)所以现在你有所有这些不同的线程和进程争夺什么可能是一个共享的大容量存储设备。 (即除非你写入24个不同的数据库,24个独立的硬盘驱动器,每个核心一个,你的进程最终将在磁盘访问时序列化。)即使你有24个不同的硬盘驱动器,写入硬盘驱动器(甚至SSD)相对较慢。您的线程将从它们运行的​​CPU中移除(以便正在等待的另一个线程可以运行)几乎任何阻塞磁盘写入。

如果你想最大化你从GCD中获得的性能,你可能想要重写你在C / C ++ / Objective-C中的子任务中所做的所有事情,将它们带入进程中,然后使用dispatch_io原语进行所有相关的I / O.对于不控制低级读写的API,您需要仔细管理和调整工作负载,以便为您的硬件优化它。例如,如果你有一堆东西写入一个共享的SQLite数据库,那么一次有多个线程试图写入该数据库就没有意义了。最好先制作一个线程(或一个串行GCD队列)来写入SQLite,并在预处理完成后将任务提交给它。

我可以在这里坚持一段时间,但最重要的是你在这里有一个复杂的,看似I / O绑定的工作流程。在最高级别,CPU利用率或“运行线程数”对于此类任务来说将是一种特别糟糕的性能衡量标准。通过使用子流程(即脚本),您可以对操作系统进行大量控制,操作系统无需事先了解您的工作负载,因此除了使用其通用调度程序来分配资源之外无法做任何事情。 GCD的不透明线程池管理实际上是您遇到的最小问题。

在实际层面上,如果您想加快速度,请购买多个,更快(即SSD)的硬盘,并重新设计您的任务/工作流程,以便单独和并行使用它们。我怀疑这会产生最大的收益(对于time == money == hardware的某种等价关系。)