使用多线程加速(std :: async,std :: thread或?)

时间:2015-08-09 19:36:13

标签: c++ multithreading asynchronous

(g ++ 4.6.3,cygwin,Windows 10)

我不确定是否有某种方法可以使用多线程机制加速以下程序(对此我很陌生):

// ab.h
class A {
    // Member variables
    ...
    // Member functions
    A();
    ~A();
    int foo_1();
    void foo_2(std::vector<int>);
    ...
}

class B {
    ...
    void schedule(std::vector<A>& va);
    ...
}

// b.cc
...
void B::schedule(std::vector<A>& va) {
    std::vector<int> vc;
    vc.resize(va.size());
    for (... /* i from 0 to vc.size() */) {
        ...
        vc[i] = va[i].foo_1();
        ...
    }
    for (... /* i from 0 to va.size() */) {
        ...
        va[i].foo_2(vc);
        ...
    }
    // 5 more pairs of "for" loops like the above block
    ...
}

// main.cc
int main() {
    ...
    std::vector<A> va;
    // va.size() can be some large like 1000000
    ...
    B b;
    int simTime = 1000000000; // some large number of iterations
    for (int clock = 0; clock != simTime; ++clock) {
        b.schedule(va);
    }
    ...
    return 0;
}

所以基本上,我有一堆A类型的对象,随着clock的增长而“前进”,同时相互沟通。我担心的是:

  1. 我刚开始使用forstd::async重写每个std::get()循环对。这有效吗?我从某个地方听说std::async最适合涉及长时间处理的函数(如I / O),因为构造/破坏线程的开销不可忽略(?)。但是,我的foo_1foo_2功能不是那么“大”。
  2. 如果构造/破坏线程很昂贵,那么最好只在开始时创建一堆线程。但在我的情况下,那将是多个“对象线程”(我猜这是不可能的)而不是“成员函数的线程”(?)。是否有可能只创建一个线程来服务一个对象,但稍后只“附加”它的不同成员函数而没有构造/破坏开销?如果是这样,怎么样?
  3. 我的代码运行时间很长(即使经过我自己的优化),同时还有一个功能强大的8核服务器......

1 个答案:

答案 0 :(得分:4)

在CPU上使用8个内核并且只使用一个内核可能会浪费资源。所以你的问题是完全合理的。由于您只提供有关您的表现问题的信息,我只能给您一些一般性的想法。

多线程不一定是解决所有性能问题的最佳方法

如果您创建线程,则需要同步访问共享信息以避免数据争用。如果需要许多这样的同步,则存在线程之间存在争用的风险(即浪费时间等待其他线程准备好资源)。这可以轻松地让您从多线程中获益。

在您的特定情况下,两个循环都访问向量va的相同元素。不幸的是,foo_1()foo2()都没有被声明为const,这意味着它们可以修改向量的元素。所以你必须仔细检查这一点。

多线程提高了吞吐量,而不是执行时间

如果你使用所有8个内核,你会发现每个内核比执行相同的非线程代码要慢一些。幸运的是,如果每个线程彼此独立并且没有争用,那么整体吞吐量将是优越的(直言不讳地说:如果2个线程执行80%的非线程,则两者一起仍然是非线程的160%)

注意:如果您创建的线程数超过CPU可以处理的数量,则其他线程将不得不等待,并将创建额外的线程管理开销。这里有一个有用的指导由thread::hardware_concurrency()给出(请记住,您的应用程序不是唯一可能创建线程的应用程序)。

武器的选择:Threadpools vs. std :: async

如果您在开头创建一个线程池,那么优势在于您已准备好触发许多线程。这可以避免在时间紧急情况下的线程创建开销。请记住,如果您处于硬件支持的线程范围内(除非您有大量的等待或IO延迟),这种好处才是真实的。

另一方面,使用std::async,您将掌握在C ++实现中。例如,MSVC上的some experimentation表明,Microsoft的异步显然重用了它创建的线程,以避免创建开销。这种方法减少了太多的线程,并且在某些情况下表现优于线程池。

<强>结论:

由于性能取决于您的算法(主要),CPU线程,操作系统,编译器+标准库,我强烈建议您选择最佳方法进行一些基准测试/分析。