C++ 对于小任务多次使用std::async是否有利于性能?

C++ 对于小任务多次使用std::async是否有利于性能?,c++,performance,asynchronous,c++11,concurrency,C++,Performance,Asynchronous,C++11,Concurrency,为了提供一些背景信息,我正在处理一个保存的文件,在使用正则表达式将文件拆分为其组件对象后,我需要根据对象的类型处理对象的数据 我目前的想法是使用并行性来获得一点性能增益,因为加载每个对象是相互独立的。因此,我打算定义一个LoadObject函数,为我要处理的每种类型的对象接受std::string,然后调用std::async,如下所示: void LoadFromFile( const std::string& szFileName ) { static const std:

为了提供一些背景信息,我正在处理一个保存的文件,在使用正则表达式将文件拆分为其组件对象后,我需要根据对象的类型处理对象的数据

我目前的想法是使用并行性来获得一点性能增益,因为加载每个对象是相互独立的。因此,我打算定义一个
LoadObject
函数,为我要处理的每种类型的对象接受
std::string
,然后调用
std::async
,如下所示:

void LoadFromFile( const std::string& szFileName )
{
     static const std::regex regexObject( "=== ([^=]+) ===\\n((?:.|\\n)*)\\n=== END \\1 ===", std::regex_constants::ECMAScript | std::regex_constants::optimize );

     std::ifstream inFile( szFileName );
     inFile.exceptions( std::ifstream::failbit | std::ifstream::badbit );

     std::string szFileData( (std::istreambuf_iterator<char>(inFile)), (std::istreambuf_iterator<char>()) );

     inFile.close();

     std::vector<std::future<void>> vecFutures;

     for( std::sregex_iterator itObject( szFileData.cbegin(), szFileData.cend(), regexObject ), end; itObject != end; ++itObject )
     {
          // Determine what type of object we're loading:
          if( (*itObject)[1] == "Type1" )
          {
               vecFutures.emplace_back( std::async( LoadType1, (*itObject)[2].str() ) );
          }
          else if( (*itObject)[1] == "Type2" )
          {
               vecFutures.emplace_back( std::async( LoadType2, (*itObject)[2].str() ) );
          }
          else
          {
               throw std::runtime_error( "Unexpected type encountered whilst reading data file." );
          }
     }

     // Make sure all our tasks completed:
     for( auto& future : vecFutures )
     {
           future.get();
     }
}
void LoadFromFile(const std::string&szFileName)
{
静态常量std::regex regexObject(“==([^=]+)===\\n((?:.| \\\\n)*)\\n===END\\1==”,std::regex_常量::ECMAScript | std::regex_常量::优化);
std::ifstream infle(szFileName);
填充异常(std::ifstream::failbit | std::ifstream::badbit);
std::string szFileData((std::istreambuf_迭代器(infle)),(std::istreambuf_迭代器());
infle.close();
向量向量机;
对于(std::sregex_迭代器itObject(szFileData.cbegin(),szFileData.cend(),regexObject),end;itObject!=end;++itObject)
{
//确定要加载的对象类型:
如果((*itObject)[1]=“Type1”)
{
emplace_back(std::async(LoadType1,(*itObject)[2].str());
}
else if((*itObject)[1]=“Type2”)
{
emplace_back(std::async(LoadType2,(*itObject)[2].str());
}
其他的
{
抛出std::runtime_错误(“读取数据文件时遇到意外类型”);
}
}
//确保我们的所有任务都已完成:
用于(汽车和未来:vecFutures)
{
future.get();
}
}
请注意,应用程序中将有两种以上的类型(这只是一个简短的示例),并且文件中可能有数千个要读取的对象

我意识到,当上下文切换超过了硬件最大并发时,创建太多线程通常是一个坏事情,但是如果我的内存服务正确,C++运行库应该监视创建的线程数量,并且安排“代码> STD::AsYNC/<代码>(我相信微软的ConcRT库应该对此负责?),那么上面的代码仍然可以提高性能吗

提前谢谢

C++运行库应该监视创建的线程数,并安排STD::Asic适当地< /P> 不可以。如果异步任务实际上是异步运行的(而不是延迟运行),那么所需要的只是它们像在新线程上一样运行。为每个任务创建并启动新线程是完全有效的,而不考虑硬件的有限并行能力

有一张便条:

[注意:如果此策略与其他策略一起指定,例如使用策略值launch::async | launch::deferred时, 实现应该推迟策略的调用或选择 当无法有效利用更多并发时。-结束说明]

然而,这是不规范的,并且在任何情况下,这表明一旦不再利用并发性,任务可能会被延迟,因此在有人等待结果时执行,而不是像ma所希望的那样,仍然是异步的,并在前一个异步任务完成后立即运行最大平行度

也就是说,如果我们有10个长时间运行的任务,而实现只能并行执行4个任务,那么前4个任务将是异步的,然后后6个任务可能会延迟。按顺序等待未来将在单个线程上按顺序执行延迟的任务,从而消除这些任务的并行执行

该注释还指出,策略的选择可能会延迟,而不是延迟调用。也就是说,该函数可能仍然异步运行,但该决定可能会延迟,比如说,直到一个早期任务完成,从而释放出一个内核以供新任务使用。但同样,这不是必需的,该注释是非规范性的,并且我知道微软的实现是唯一一个这样做的。当我查看另一个实现libc++时,它完全忽略了这个注释,因此使用
std::launch::async
std::launch::any
策略会导致在新线程上异步执行

(我相信微软的ConcRT库应该对此负责?)

微软的实现确实如您所描述的那样,但是这不是必需的,并且便携式程序不能依赖于这种行为

可移植地限制实际运行的线程数量的一种方法是使用类似于信号量的东西:

#include <future>
#include <mutex>
#include <cstdio>

// a semaphore class
//
// All threads can wait on this object. When a waiting thread
// is woken up, it does its work and then notifies another waiting thread.
// In this way only n threads will be be doing work at any time.
// 
class Semaphore {
private:
    std::mutex m;
    std::condition_variable cv;
    unsigned int count;

public:
    Semaphore(int n) : count(n) {}
    void notify() {
        std::unique_lock<std::mutex> l(m);
        ++count;
        cv.notify_one();
    }
    void wait() {
        std::unique_lock<std::mutex> l(m);
        cv.wait(l, [this]{ return count!=0; });
        --count;
    }
};

// an RAII class to handle waiting and notifying the next thread
// Work is done between when the object is created and destroyed
class Semaphore_waiter_notifier {
    Semaphore &s;
public:
    Semaphore_waiter_notifier(Semaphore &s) : s{s} { s.wait(); }
    ~Semaphore_waiter_notifier() { s.notify(); }
};

// some inefficient work for our threads to do
int fib(int n) {
    if (n<2) return n;
    return fib(n-1) + fib(n-2);
}

// for_each algorithm for iterating over a container but also
// making an integer index available.
//
// f is called like f(index, element)
template<typename Container, typename F>
F for_each(Container &c, F f) {
    Container::size_type i = 0;
    for (auto &e : c)
        f(i++, e);
    return f;
}

// global semaphore so that lambdas don't have to capture it
Semaphore thread_limiter(4);

int main() {
    std::vector<int> input(100);
    for_each(input, [](int i, int &e) { e = (i%10) + 35; });

    std::vector<std::future<int>> output;
    for_each(input, [&output](int i, int e) {
        output.push_back(std::async(std::launch::async, [] (int task, int n) -> int {
            Semaphore_waiter_notifier w(thread_limiter);
            std::printf("Starting task %d\n", task);
            int res = fib(n);
            std::printf("\t\t\t\t\t\tTask %d finished\n", task);
            return res;
        }, i, e));
    });

    for_each(output, [](int i, std::future<int> &e) {
        std::printf("\t\t\tWaiting on task %d\n", i);
        int res = e.get();
        std::printf("\t\t\t\t\t\t\t\t\tTask %d result: %d\n", i, res);
    });
}
#包括
#包括
#包括
//信号量类
//
//所有线程都可以在此对象上等待
//被唤醒后,它执行其工作,然后通知另一个等待的线程。
//这样,在任何时候只有n个线程在做工作。
// 
类信号量{
私人:
std::互斥m;
std::条件变量cv;
无符号整数计数;
公众:
信号量(int n):计数(n){}
作废通知(){
std::唯一锁l(m);
++计数;
cv.通知_one();
}
无效等待(){
std::唯一锁l(m);
等待(l,[this]{返回计数!=0;});
--计数;
}
};
//处理等待和通知下一个线程的RAII类
//在创建对象和销毁对象之间完成工作
类信号量\服务员\通知程序{
信号量&s;
公众:
信号量通知器(信号量&s):s{s}{s.wait();}
~Semaphore_water_notifier(){s.notify();}
};
//我们公司的一些低效工作