线程上的简单分工并没有减少花费的时间

代码_饲料

我一直试图通过将工作拆分为任务/线程来改善项目的计算时间,但效果不佳。所以我决定做一个简单的测试项目,看看我是否可以让它在一个非常简单的情况下工作,这也没有像我预期的那样工作。

我试图做的是:

  • 在一个线程中执行 X 次任务 - 检查花费的时间。
  • 在 Y 线程中执行 X / Y 次任务 - 检查所用时间。

因此,如果 1 个线程需要 T 秒来执行 100'000'000 次“工作”迭代,那么我会期望:

  • 2 个线程执行 50'000'000 次迭代,每个需要 ~ T / 2 秒
  • 3 个线程执行 33'333'333 次迭代,每个需要 ~ T / 3 秒

依此类推,直到达到某个线程限制(内核数或其他)。

因此,我编写了代码并在我的 8 核系统 (AMD Ryzen) 上进行了测试,该系统的 RAM > 16GB,当时什么也不做。

  • 1 线程花费:~6.5s
  • 2 线程花费:~6.7s
  • 3 个线程耗时:~13.3 秒
  • 8 个线程耗时:~16.2s

很明显,这里有些不对劲!

我将代码移植到 Godbolt 中,我看到了类似的结果。Godbolt 只允许 3 个线程,对于 1、2 或 3 个线程,运行时间大约为 8 秒(相差约 1 秒)。这是 Godbolt 实时代码:https ://godbolt.org/z/6eWKWr

最后附上代码供参考:

#include <iostream>
#include <math.h>
#include <vector>
#include <thread>

#define randf() ((double) rand()) / ((double) (RAND_MAX))

void thread_func(uint32_t interations, uint32_t thread_id)
{
    // Print the thread id / workload
    std::cout << "starting thread: " << thread_id << " workload: " << interations << std::endl;
    // Get the start time
    auto start = std::chrono::high_resolution_clock::now();
    // do some work for the required number of interations
    for (auto i = 0u; i < interations; i++)
    {
        double value = randf();
        double calc = std::atan(value);
        (void) calc;
    }
    // Get the time taken
    auto total_time = std::chrono::high_resolution_clock::now() - start;
    // Print it out
    std::cout << "thread: " << thread_id << " finished after: "
              << std::chrono::duration_cast<std::chrono::milliseconds>(total_time).count()
              << "ms" << std::endl;
}

int main()
{
    // Note these numbers vary by about probably due to godbolt servers load (?)
    // 1 Threads takes: ~8s
    // 2 Threads takes: ~8s
    // 3 Threads takes: ~8s
    uint32_t num_threads = 3; // Max 3 in godbolt
    uint32_t total_work = 100'000'000;

    // Seed rand
    std::srand(static_cast<unsigned long>(std::chrono::steady_clock::now().time_since_epoch().count()));

    // Store the start time
    auto overall_start = std::chrono::high_resolution_clock::now();

    // Start all the threads doing work
    std::vector<std::thread> task_list;
    for (uint32_t thread_id = 1; thread_id <= num_threads; thread_id++)
    {
        task_list.emplace_back(std::thread([=](){ thread_func(total_work / num_threads, thread_id); }));
    }

    // Wait for the threads to finish
    for (auto &task : task_list)
    {
        task.join();
    }

    // Get the end time and print it
    auto overall_total_time = std::chrono::high_resolution_clock::now() - overall_start;
    std::cout << "\n==========================\n"
              << "thread overall_total_time time: "
              << std::chrono::duration_cast<std::chrono::milliseconds>(overall_total_time).count()
              << "ms" << std::endl;
    return 0;
}

注意:我尝试使用 std::async 也没有任何区别(不是我期待的)。我也尝试编译发布 - 没有区别。

我读过这样的问题:为什么使用更多线程使它比使用更少线程更慢,我看不到一个明显的(对我来说)瓶颈:

  • CPU bound (needs lots of CPU resources): 我有8核
  • Memory bound (needs lots of RAM resources):我已经为我的 VM 分配了 10GB 内存,没有运行其他任何东西
  • I/O bound (Network and/or hard drive resources): 不涉及网络流量
  • 这里没有睡眠/互斥(就像在我的真实项目中一样)

问题是:

  • 为什么会发生这种情况?
  • 我究竟做错了什么?
  • 我该如何改进?
1201程序报警

rand不保证函数是线程安全的。看来,在您的实现中,它是通过使用锁或互斥锁来实现的,因此如果多个线程试图轮流生成随机数。由于您的循环主要只是调用rand,因此多线程会影响性能。

您可以使用<random>标头的设施并让每个线程使用它自己的引擎来生成随机数。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

Div Previous Siblings并没有减少它的大小

为什么 dopar 没有减少时间?

Ubuntu注意到事情并没有同步,但是实际上并没有同步

EMR上的火花:当节点数增加时,在EMR中运行数据的时间没有减少

新线并没有被淘汰

Keras损失没有减少

Bootstrap Datepicker扩大但没有减少

Keras NN损失没有减少

当任务在 Java 中完成时,线程正在增加但没有减少

C 在 Mac 上:尝试使用“打开”实际上并没有打开 url

Redis集群设置,并没有在所有节点上发生

当捕捉实际上并没有捕捉到任何

kill 实际上并没有终止幽灵进程

Goroutine 安全通道关闭实际上并没有关闭 webscoket

docker在Mac上运行ubuntu并没有任何反应

尝试在Windows上使用bash并没有安装分发消息

分片集群似乎并没有基于mongoDB原型上的键来划分数据

border none 属性并没有真正移除边框(视觉上)

登录后被重定向,但我实际上并没有登录 Spring Security

cpulimit实际上并没有限制CPU使用率

PDO表示会更新表格,但实际上并没有

git 实际上并没有保存 Unity 资产

TextField controller.clear() 实际上并没有清除 TextField

Mongoose model.update 和 model.findOneAndUpdate 实际上并没有更新

停止调试实际上并没有停止站点应用程序

cd实际上并没有进入Makefile目标中的目录

“新阵列”上的forEach并没有达到我的期望

替换 LinkedList 中的节点实际上并没有替换任何东西

在 RealmRecyclerViewAdapter 上调用 updateData 实际上并没有更新数据