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

Objective c 如何加快Mac应用程序处理5000个独立任务的速度?,objective-c,xcode,macos,parallel-processing,grand-central-dispatch,Objective C,Xcode,Macos,Parallel Processing,Grand Central Dispatch,我有一个长时间运行(5-10小时)的Mac应用程序,可以处理5000个项目。通过执行大量转换(使用Saxon)、运行一组脚本(使用Python和Racket)、收集数据并将其序列化为一组XML文件、SQLite数据库和CoreData数据库来处理每个项目。每个项目都完全独立于其他项目 总之,它做了很多事情,需要很长时间,并且看起来是高度可并行的 加载所有需要处理的项目后,应用程序使用GCD并行化工作,使用dispatch\u apply: dispatch_apply(numberOfItems

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

总之,它做了很多事情,需要很长时间,并且看起来是高度可并行的

加载所有需要处理的项目后,应用程序使用GCD并行化工作,使用
dispatch\u apply

dispatch_apply(numberOfItems, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^(size_t i) {
    @autoreleasepool {
        ...
    }
});
我正在Mac Pro上运行这个应用程序,它有12个内核(24个虚拟内核)。所以我希望在任何时候都有24件物品被处理。然而,通过日志记录,我发现正在处理的项目数量在8到24之间变化。这实际上是在运行时间上增加了几个小时(假设它一次可以处理24个项目)

一方面,也许GCD真的非常聪明,它已经给了我最大的吞吐量。但我担心的是,因为大部分工作都是在这个应用程序生成的脚本中进行的,可能GCD是根据不完整的信息进行推理,并没有做出最佳决策

如何提高绩效有什么想法吗?在正确性之后,第一个需要的属性是缩短这个应用程序运行的时间。我不在乎耗电量、占用Mac Pro或其他任何东西

更新:事实上,这在以下方面看起来令人担忧:“并发队列在任何给定时刻执行的实际任务数都是可变的,并且可以随着应用程序条件的变化而动态变化。许多因素会影响并发队列执行的任务数量,包括可用内核的数量,其他进程正在完成的工作量,以及其他串行调度队列中任务的数量和优先级。”(重点已添加)看起来让其他进程工作会对应用程序中的调度产生不利影响


如果能够说“并发运行这些块,每个核心一个,不要尝试做任何更聪明的事情”,那就太好了“

如果已绑定并确定,则可以使用NSThread API显式生成24个线程,并让每个线程从同步的工作项队列中提取。我敢打赌,业绩会明显恶化

当提交给GCD的工作项从不阻塞时,GCD的工作效率最高。也就是说,您所描述的工作负载相当复杂,并且充满了线程阻塞的机会。首先,您正在生成一系列其他流程。在这里,这意味着您已经依赖操作系统在主任务和这些从任务之间分配时间/资源。除了设置每个子进程的操作系统优先级外,操作系统调度程序无法知道哪些进程比其他进程更重要,默认情况下,子进程将具有与其父进程相同的优先级。也就是说,通过调整流程优先级,您似乎没有任何收获。我假设您正在阻止等待从属任务完成的主任务线程。这就是有效地停止线程——它不能做任何有用的工作。但正如我所说,我认为调整从任务的操作系统优先级没有什么好处,因为这听起来像是一个受I/O约束的工作流

接下来,我们将描述三个I/O操作(“将其序列化为一组XML文件、一个SQLite数据库和一个CoreData数据库”),因此,现在所有这些不同的线程和进程都在争夺一个共享大容量存储设备。(即,除非您在24个单独的硬盘驱动器(每个核心一个)上写入24个不同的数据库,否则您的进程最终将在磁盘访问时序列化。)即使您有24个不同的硬盘驱动器,写入硬盘驱动器(即使是SSD)的速度也相对较慢。您的线程将被从运行它们的CPU上取下(以便等待的另一个线程可以运行),以进行几乎任何阻塞磁盘写入

如果您想最大限度地提高GCD的性能,您可能需要在C/C++/Objective-C中重写子任务中所做的所有工作,将它们带到进程中,然后使用
dispatch\u io
原语执行所有相关的I/O。对于不控制低级读写的API,您需要仔细管理和调整工作负载,以针对现有硬件进行优化。例如,如果您有一堆东西要写入一个共享的SQLite数据库,那么一次尝试写入该数据库的线程就没有意义了。您最好让一个线程(或一个串行GCD队列)写入SQLite,并在预处理完成后向该线程提交任务

我可以在这里讲一段时间,但归根结底,这里有一个复杂的、似乎受I/O限制的工作流。在最高级别上,CPU利用率或“正在运行的线程数”将是衡量此类任务性能的一个特别差的指标。通过使用子进程(即脚本),您将大量控制权交给了操作系统,而操作系统实际上事先对您的工作负载一无所知,因此除了使用其通用调度程序来分配资源外,什么也做不了。GCD的不透明线程池管理实际上是您的最小问题


在实践层面上,如果您想加快速度,请购买多个速度更快(即SSD)的硬盘驱动器,并重新设计您的任务/工作流程,以单独和并行地使用它们。我怀疑这会给你带来最大的收益(对于
时间==金钱==硬件的一些等价关系)

如果你使用不同的自定义队列而不是