【问题标题】:std::thread won't get my cpu over 20% load?std::thread 不会让我的 CPU 负载超过 20%?
【发布时间】:2019-03-20 08:52:18
【问题描述】:

也许我使用 std::thread 错误? 我有一个 12 线程 cpu 并构建了一个渲染引擎。更像是一个绘图仪,用于诸如柏林噪声等之类的东西...... 由于渲染速度很慢,我使用 std::thread 来充分利用我的 12 个内核。渲染速度实际上大大提高了,但我的 cpu 使用率不会比只使用一个线程高......

这是我负责渲染的代码……让我试着解释一下……

void Renderer::Render(sf::Image * _img,
                      sf::Vector2f _dims,
                      unsigned _threadCount)
{
    im = _img;
    dims = _dims;

    im->create(dims.x, dims.y, sf::Color(100, 0, 0, 255));

    static float bar = 0.3f;
    float g = 0.01f;

    std::vector < std::thread > workers;

    for (unsigned i = 0; i < _threadCount; ++i)
    {
        workers.push_back(std::thread(Renderer::RenderThread,
                                      sf::IntRect((dims.x / _threadCount) * i,
                                                  0,
                                                  (dims.x / _threadCount) * (i + 1),
                                                  dims.y),
                                      bar,
                                      g,
                                      sf::Color(0,
                                                (255.0f / _threadCount) * i,
                                                0)));
    }

    bar += 0.01f;

    for (unsigned i = 0; i < workers.size(); ++i)
    {
        workers[i].join();
    }

    return;
}

void Renderer::RenderThread(sf::IntRect area, float bar, float g, sf::Color clr)
{
    for (unsigned x = area.left + 1; x < area.width + 1; x++)
    {
        for (unsigned y = area.top + 1; y < area.height + 1; y++)
        {
            im->setPixel(x - 1, y - 1, ImAlg::Tiles0(x, y, bar, 5));
        }
    }

    return;
}

所以基本上它的作用如下: 上层函数是主函数,由框架调用。 它创建线程。 较低的函数是在每个线程上运行的函数。 我试图将要渲染的图像分成均匀的部分。 就像如果图像是 1000 宽并且我会使用 4 个线程,每个线程都会渲染 250x1000 像素

【问题讨论】:

  • 您在测试发布/优化构建吗?我见过在调试模式下花费 100 倍的情况。
  • Render::Render 中,您每次调用都会创建和销毁_threadCount 线程。创建线程是一项非常昂贵的任务。研究使用线程池并将工作任务交给他们。
  • 可能是图像太小,以至于创建这些线程的成本对于每个线程中完成的工作量来说非常重要。
  • 使用现代 C++(发布史诗般的复兴是 C++11)的线程池,用于将工作项分派到可重用的已经运行线程只是等待某事做只需要几十行代码。认真考虑一下理查德在说什么。
  • 也许您需要告诉我们您使用什么工具来确定“20% 负载”。使用“top”实用程序(Ubuntu 18.04):我的“Hailstone”C++ 应用程序显示 100% 使用 1 个线程(运行时间 44 秒),(不同的运行)200% 使用两个线程(耗时 22 秒)。 [在我较旧的两核桌面上]。你的代码(你分享的)在我看来没问题。

标签: c++ windows multithreading rendering sfml


【解决方案1】:

不要使用原始线程。从一个简单的线程池开始:

struct thread_pool {
  std::future<void> run( std::function<void()> f ){
    std::packaged_task<void()> t(std::move(f));
    auto r = t.get_future();
    auto l = lock();
    q.push_back(std::move(t));
    cv.notify_one();
    return r;
  }
  void start(std::size_t n){
    //std::cout << n << " threads starting" << std::endl;
    while(n--)
      threads.push_back( std::thread([this]{loop();}) );
   }
  ~thread_pool(){
    end();
    for (auto&&t:threads)t.join();
  }
private:
  void end(){
    auto n=threads.size();
    auto l=lock();
    for(auto count=n;count;--count)
      q.emplace_back();
    cv.notify_all();
    //std::cout << n << " threads ended" << std::endl;
  }
  void loop(){
    while(true){
      //std::cout << "Waiting..." << std::endl;
      auto t=pop();
      //std::cout << "Got " << t.valid() << std::endl;
      if (!t.valid()) break;
      //std::cout << "Running..." << std::endl;
      t();
    }
  }
  std::packaged_task<void()> pop(){
    auto l=lock();
    cv.wait(l, [&]{ return !q.empty();});
    auto r = std::move(q.front());
    q.pop_front();
    return r;
  }
  std::vector<std::thread> threads;

  std::unique_lock<std::mutex> lock(){ return std::unique_lock<std::mutex>(m); }
  std::mutex m;
  std::deque<std::packaged_task<void()>> q;
  std::condition_variable cv;
};

Live example.

使用线程池;传入需要线程的代码,并预先启动线程(可能基于硬件并发)。 run 任务。等待 run 方法返回的未来。

这将比按需启动线程快得多。

【讨论】:

    猜你喜欢
    • 2015-11-08
    • 2013-09-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-29
    • 2012-06-21
    • 1970-01-01
    相关资源
    最近更新 更多