5

我有一个简单的程序可以执行一些蒙特卡洛算法。该算法的一次迭代没有副作用,因此我应该能够使用多个线程运行它。所以这是我整个程序的相关部分,它是用 C++11 编写的:

void task(unsigned int max_iter, std::vector<unsigned int> *results, std::vector<unsigned int>::iterator iterator) {
    for (unsigned int n = 0; n < max_iter; ++n) {
        nume::Album album(535);
        unsigned int steps = album.fill_up();
        *iterator = steps;
        ++iterator;
    }
}

void aufgabe2() {
    std::cout << "\nAufgabe 2\n";

    unsigned int max_iter = 10000;

    unsigned int thread_count = 4;

    std::vector<std::thread> threads(thread_count);
    std::vector<unsigned int> results(max_iter);

    std::cout << "Computing with " << thread_count << " threads" << std::endl;

    int i = 0;
    for (std::thread &thread: threads) {
        std::vector<unsigned int>::iterator start = results.begin() + max_iter/thread_count * i;
        thread = std::thread(task, max_iter/thread_count, &results, start);
        i++;
    }

    for (std::thread &thread: threads) {
        thread.join();
    }

    std::ofstream out;
    out.open("out-2a.csv");
    for (unsigned int count: results) {
        out << count << std::endl;
    }
    out.close();

    std::cout << "Siehe Plot" << std::endl;
}

令人费解的是,我添加的线程越多,它就越慢。有4个线程,我得到这个:

real    0m5.691s
user    0m3.784s
sys     0m10.844s

而单线程:

real    0m1.145s
user    0m0.816s
sys     0m0.320s

我意识到在 CPU 内核之间移动数据可能会增加开销,但vector应该在启动时声明,而不是在中间修改。是否有任何特殊原因导致多核上的速度变慢?

我的系统是 i5-2550M,它有 4 个内核(2 + 超线程),我使用 g++ (Ubuntu/Linaro 4.7.3-1ubuntu1) 4.7.3

更新

我看到不使用线程 (1),它将有很多用户负载,而使用线程 (2),它将具有比用户负载更多的内核:

10K 次运行:

http://wstaw.org/m/2013/05/08/stats3.png

10 万次运行:

http://wstaw.org/m/2013/05/08/Auswahl_001.png

当前的main.cpp

运行 10 万次后,我得到以下信息:

根本没有线程:

real    0m28.705s
user    0m28.468s
sys     0m0.112s

程序的每个部分都有一个线程。这些部分甚至不使用相同的内存,所以我对同一个容器的并发性也应该被淘汰。但这需要更长的时间:

real    2m50.609s
user    2m45.664s
sys     4m35.772s

因此,虽然三个主要部分占用了我的 CPU 的 300%,但它们占用的时间是原来的 6 倍。

运行 100 万次,就可以了real 4m45real 20m我之前跑过 1M ,如果没有,至少也需要real 30m.

4

2 回答 2

5

在 GitHub 评估您当前的 main.cpp。除了上面提供的评论:

  1. 是的, rand() 不是线程安全的,因此在运行多线程业务逻辑之前,可能值得用随机值预先填充一些数组(这样可以减少可能的锁数量)。如果您计划进行一些堆活动(在多线程之前进行预分配或使用自定义的每线程分配器),那么内存分配也是如此。
  2. 不要忘记其他过程。如果您计划在 4 个内核上使用 4 个线程,这意味着您将与其他软件(至少是操作系统例程)竞争 CPU 资源。
  3. 文件输出是一个大储物柜播放器。您在每次循环迭代中执行“<<”运算符,这会花费您很多(我记得我过去的一个有趣案例:执行日志输出间接修复了一个多线程错误。因为通用记录器是锁驱动的,所以它是某种同步原语,请注意!)。
  4. 最后,没有任何形式的保证多线程应用程序可以比单线程更快。有一堆特定于 CPU 的、特定于环境的等方面。
于 2013-05-10T12:35:54.777 回答
1

矢量对象结果由创建的所有线程共享,因此即使您的问题是令人尴尬的并行,由于共享对象,存在争用更不用说缓存未命中(我不足以解释现代架构上的缓存)。可能您的 n 个线程应该有 n 个结果向量,最后合并结果。那会加快速度,我猜。

另一个要提到的技巧是尽可能使用 std::async 而不是线程。它处理线程分配和其他低级混乱。我从 Scott Mayer 的有效 c++11 书中读到它。但是,通过使用线程,您可以将线程关联设置到特定核心。所以,如果你的处理器支持 8 个线程,那么至少在 linux 上你可以创建 8 个线程并将每个线程分配给每个内核。

于 2016-08-08T06:43:41.173 回答