首页 > 解决方案 > 在 iOS 设备上执行并发任务时如何设置合适的线程数?

问题描述

我开发了一个跨平台的 c++ 库,它在运行时产生线程。我使用并发队列来调度计算任务,因此每个线程大部分时间都会很忙。

现在的问题是如何在运行时获得适当数量的线程。由于我的任务没有 I/O 或网络操作,只有计算和堆内存分配,所以最好的策略是为每个 CPU 内核生成线程:

我的代码如下所示:

#include "concurrentqueue.h"
#include <algorithm>
#include <thread>
#include <vector>
#include <iostream>
#include <mutex>

std::mutex io_m;

struct Task {
    int n;
};

void some_time_consuming_operations(Task &t) {
    std::vector<int> vec;
    for (int i = 0; i < t.n; ++i)
        vec.push_back(1);
    {
        std::lock_guard<std::mutex> g(io_m);
        std::cout << "thread " << std::this_thread::get_id() << " done, vec size:" << vec.size() << std::endl;
    }
}

int main() {
    // moodycamel's lockfree queue: https://github.com/cameron314/concurrentqueue
    moodycamel::ConcurrentQueue<Task> tasks;

    for (int i = 0; i < 100; ++i)
        tasks.enqueue(Task{(i % 5) * 1000000 + 1000000});

    // I left 2 threads for ui and other usages
    std::vector<std::thread> jobs(std::max((size_t)2, (size_t)std::thread::hardware_concurrency() - 2));

    std::cout << "thread num:" << jobs.size() << std::endl;

    for (auto &job : jobs) {
        job = std::thread([&tasks]() {
            Task task;
            while (tasks.try_dequeue(task))
                some_time_consuming_operations(task);
        });
    }

    for (auto &job : jobs)
        job.join();

    return 0;
}

但是,在我的 iOS 设备(iPhone XR、A12)上启用多线程时,测试程序比单线程模式慢 2 倍。我已经在我的 windows 机器上用 4 核 8 线程 intel CPU 对其进行了测试,它比单线程模式快 6 倍。

在我的 iPhone 上,该hardware_concurrency函数返回6,这正是 Apple A12 的核心编号。在我的 Windows 机器上,数字是 8。

我知道 Apple 的 A12 有 4 个称为 Tempest 的节能核心,但因为他们声称A11/A12 将同时使用所有六个核心(我在测试期间保持充电)。我不知道为什么它比单线程模式慢。

测试程序是UE4构建的游戏应用。

标签: c++iosmultithreadingconcurrency

解决方案


四个较慢的核心比快速核心慢很多。因此,如果您在一个快速核心上执行一项需要 6 秒的任务,并在每个核心上运行一秒钟的工作,那么两个快速核心将在一秒钟后完成,而四个慢速核心可能需要 10 秒。

如果您使用 GCD,iOS 将在内核之间混洗这六个线程,因此您可以获得高达 2.4 倍的速度。如果您的线程实现不这样做,那么您正在减慢速度。

解决方案:要么使用 GCD(并获得 2.4 的加速),要么仅使用两个线程(并获得 2.0 的加速)。那是在 iPhone XR 上;您需要以某种方式找出快速核心的数量。


推荐阅读