【发布时间】:2013-06-16 07:00:58
【问题描述】:
为了提供一些背景信息,我正在处理一个保存的文件,在使用正则表达式将文件拆分为其组件对象后,我需要根据对象的类型来处理对象的数据。
我目前的想法是使用并行性来获得一点性能提升,因为加载每个对象是彼此独立的。所以我要定义一个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();
}
}
请注意,应用程序中将有超过 2 种类型(这只是一个简短的示例),并且文件中可能有数千个要读取的对象。
我知道创建太多线程通常对性能不利,因为上下文切换会超过最大硬件并发,但如果我的记忆正确,C++ 运行时应该监控创建的线程数和适当地安排std::async(我相信微软的情况是他们的ConcRT库对此负责?),所以上面的代码仍然可能导致性能提升?
提前致谢!
【问题讨论】:
-
上述代码可能实际上会带来性能提升,但我想说这取决于每个
LoadTypeX所做的工作量。是否足以超过您在主线程中启动、等待和同步的开销?更不用说缓存未命中和错误共享的数量增加了。以及与多线程编程相关的其他惩罚。因此,如果您的对象很大并且您的异步加载函数正在做大量工作,我会说这可能是值得的。但是你为什么不直接测量呢? -
不相关:您正在创建一个包含 100 个默认构造的期货的向量,然后在最后附加您的真实期货。在这些默认构造的期货上调用
get()会导致未定义的行为。 -
你分析过你的代码吗?我原本预计 I/O 成本会使处理成本相形见绌,以至于将处理拆分为线程所带来的收益可能无法衡量。
-
严格来说,没有办法知道。您不知道
std::async如何或何时运行任务。你只知道当future::get返回时,结果就准备好了。该任务可能在另一个线程或纤程中异步运行,或者在您调用get时它甚至可能同步运行。后者是一种“作弊”,但这是可以允许的。
标签: c++ performance asynchronous c++11 concurrency