4

我有一个长时间运行(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 或其他任何事情。

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

能够说“同时运行这些块,每个内核一个,不要尝试做任何更聪明的事情”会很好。

4

1 回答 1

6

如果您有约束力和决心,您可以使用 NSThread API 显式生成 24 个线程,并让每个线程从同步的工作项队列中拉取。我敢打赌,性能会明显变差。

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

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

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

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

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

于 2013-08-15T11:46:16.293 回答