如何创建和管理足够的参与者以最大程度地利用Swift中的CPU使用?
我经常发现自己必须对单个资源进行大量操作,例如将图像调整为许多不同的分辨率。当所讨论的资源为soddable
(例如,使用cgimage
),我可以使用withTaskGroup
并为每个创建一个子任务我需要执行的操作,它可以最大化CPU用法(只要soddable
资源不执行自己的同步以防止并发操作)。
但是,有时,资源是不是 发送
(例如cgpdfpage
)。在这些情况下,我可能会做三件事之一:
- 只需完全避免使用任务,而只需执行我需要在单个线程上的标准循环中执行的操作即可。
- 将非隔离资源包裹在
actor
中,然后继续使用withTaskGroup
为我要执行的每个操作创建子任务(这将在Actor上称为隔离方法)。 - 使用
withTaskGroup
为我要执行的每个操作创建子任务,在每个任务中初始化资源的新副本。
方法1和2 基本上具有相同的结果:即使在方法2中使用withTaskGroup
,由于参与者的孤立性质,一切仍会同步发生。
方法3 确实使我可以最大化CPU,但是每当我想在其上执行操作时,它都需要初始化资源,这可能是数千次,并且初始化可能会有巨大的成本该资源(例如从文件系统加载它)。即使这样,尽管增加了成本,但它仍然可能更快地完成操作(如果不是特别优化的话)。
理想情况下,我想创建足够的资源/参与者来最大化CPU使用(例如,如果机器有八个内核,我可能想创建八个资源,每个资源都包裹在一个演员的实例中)。然后,我将进行数千个操作,为每个操作创建一个任务。在每个任务中,它将从“演员池”中选择一个闲置的演员来执行操作。
这是做我想做的事情的最好方法吗?如果是这样,我该如何有效地管理所谓的“演员池”,以确保仅创建所需的资源数量?
I often find myself having to perform lots of operations on a single resource, e.g. resizing an image to lots of different resolutions. When the resource in question is Sendable
(which for example is the case with CGImage
), I can use withTaskGroup
and create a child task for each operation I need to perform, which maximises the CPU usage (as long as the Sendable
resource in question doesn't perform its own synchronisation to prevent concurrent operations).
Sometimes, however, the resource is not Sendable
(e.g. CGPDFPage
). In these cases I might do one of either three things:
- Simply avoid the use of tasks altogether, and just perform the operations I need to perform within a standard loop on a single thread.
- Wrap the non-sendable resource within an
actor
, and continue to usewithTaskGroup
to create child tasks for each operation I want to perform (which would call an isolated method on the actor). - Use
withTaskGroup
to create child tasks for each operation I want to perform, initialising a fresh copy of the resource within each task.
Methods 1 and 2 have basically the same result: even by using withTaskGroup
in method 2, everything will still happen synchronously because of the isolated nature of actors.
Method 3 does allow me to maximise the CPU, but it requires initialising the resource every time I want to perform an operation on it, which might be thousands of times, and there can be a significant cost to initialising that resource (such as loading it from the filesystem). Even so, despite this added cost, it might still complete the operations faster (if not in particularly optimised way).
Ideally, I want to create just enough resources/actors to maximise CPU usage (e.g. if the machine has eight cores, I'd probably want to create eight resources, each one wrapped within an instance of an actor). Then, I'd do the thousands of operations, creating a task for each one. In each task it would pick an idle actor from the "actor pool" to perform the operation on.
Is this the best way of doing what I'm trying to do? If so, how can I effectively manage the so-called "actor pool", ensuring that it creates only the number of resources required?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
关于“最大化CPU使用”,您可能已经知道这一点,但是Swift并发具有“合作线程池”,它将在超过设备功能的同时最大化CPU使用情况。因此,如果您的处理任务是同步的,只需将任务添加到任务组,它将避免线程爆炸,而且还可以完全利用您的CPU。
关于您在各种方法上的问题,很难抽象回答这个问题。并发引入了一定数量的间接费用。与初始化和同步开销相比,目标是最大化并发任务中完成的计算工作量。
关于您的单个和多线程问题(即选项1),我们必须认识到,有些过程根本不适合并行性,因为从同时发生中获得的收益不足以抵消其引入的开销。目的是在添加到组中的每个任务中执行尽可能多的无量计算工作,并将同步(Actor隔离工作)限制为最小值。
关于多演员方法,我很难想象一个场景,在这种情况下,这将使单个演员受益。我们的目标是尽可能最大程度地减少同步,并添加两个同步层似乎只会使它变得更糟(并且更复杂)。我可能会在两层同步模式之前进行大步证明(例如,而不是10,000个任务,每个构建一个页面,也许每个构建100页的任务)。
但是,如果您要处理低计算复杂性与共享资源的高态度的结合,那么单线程解决方案实际上可能最终会更快。有时,您只需要这样做,然后在确定之前对其进行基准测试。
我们可能需要一个 mcve 才能进一步帮助您。
Regarding “maximizing CPU usage”, you may know this already, but Swift concurrency has a “cooperative thread pool”, which will maximize CPU usage while exceeding the device’s capabilities. So, if your processing task is synchronous, just add tasks to your task group, and it will avoid thread explosion, but also fully utilize your CPU.
Regarding your question on various approaches, it is very hard to answer this question in the abstract. There is a certain amount of overhead introduced by concurrency. The goal is to maximize the amount of computational work done in the concurrent task as compared to the initialization and synchronization overhead.
Regarding your single vs. multithreaded question (i.e., option 1), we must recognize that there are some processes that simply do not lend themselves to parallelism, for which the gains from concurrency are not sufficient to offset the overhead it introduces. The goal is to perform as much uncontended computational work in each task added to the group, and limit the synchronization (the actor isolated work) to the minimum.
Regarding the multi-actor approach, I am hard-pressed to imagine a scenario where that will yield benefits over a single actor. Our goal is to minimize the synchronization as much as possible, and adding two tiers of synchronization seems like it would only make it worse (and more complicated). I would probably pursue striding before two-tier synchronization pattern (e.g., rather than 10,000 tasks building one page each, perhaps 100 tasks building 100 pages each).
But if you are dealing with a combination of low-computational complexity with high-contention of shared resources, a single-threaded solution might actually end up being faster. Sometimes you just have to do it both ways and benchmark it before you can be sure.
We probably need a MCVE to assist you further.