接受迭代器对的函数在并行运行时不工作
Function taking iterator pair not working when run in parallel
我有下面的模板函数,它接受一个GameName (std::string)和一个开始/结束迭代器对GameTime (size_t)集合。它在范围内迭代并将GameTime-s加在一起,并返回一个包含游戏名称、总游戏时间和平均游戏时间(GameStats)的元组:
template<typename InputIt>
GameStats calculateGameStats(const GameName& _gameName, const InputIt _begin, const InputIt _end)
{
std::string logMessage = "Started process for game " + _gameName + ":[ ";
for_each(_begin,_end,[&logMessage](GameTime e){ logMessage += std::to_string(e) + ',';});
std::clog << logMessage + " ]n";
size_t itemCount = 0;
GameTime gameTime =
std::accumulate(_begin,_end,0,[&itemCount](const GameTime _lhs, const GameTime _rhs)
{
++itemCount;
return _lhs + _rhs;
});
logMessage = "Ended process for game " + _gameName + ":[ ";
for_each(_begin,_end,[&logMessage](GameTime e){ logMessage += std::to_string(e) + ',';});
std::clog << logMessage + " ]n";
return std::make_tuple(_gameName, gameTime, gameTime / itemCount);
}
(出于调试目的,我还列出了我们在开始和结束时迭代的元素。显然,它们每次都应该相同,但见下文。
这个函数的类似版本接受std::vector的引用,工作得很好,但是这个新函数在并行运行多个实例时似乎会弄乱它的迭代器。以下是为每个游戏启动进程的代码:
// Start processing each game's stats in parallel
std::vector< std::future<GameStats> > processVector;
processVector.reserve(gameCount);
for(const std::pair<GameName, std::vector<GameTime> >& entryListPair : gameEntries)
{
const std::string& gameName = entryListPair.first;
const std::vector<GameTime>& entryList = entryListPair.second;
processVector.push_back(std::async(std::launch::async,
&calculateGameStats<decltype(entryList.cbegin())>,
gameName,
entryList.cbegin(),
entryList.cend()));
assert((processVector.cend()-1)->valid());
}
(GameEntries是一个std::map类型,将GameName映射到GameTime的向量)
下面是运行程序输出的相关部分:
Started process for game CoD:[ 182,1264, ]
Ended process for game CoD:[ 606,1667, ]
Started process for game DotA:[ 606,1667, ]
Ended process for game DotA:[ 606,1667, ]
Started process for game GTAV:[ 606, ]
Ended process for game GTAV:[ 606, ]
Started process for game HotS:[ 606, ]
Ended process for game HotS:[ 606, ]
Started process for game LoL:[ 1277,193, ]
Ended process for game LoL:[ 1277,193, ]
Started process for game MC:[ 857,193, ]
Ended process for game MC:[ 857,193, ]
Started process for game OW:[ 0, ]
Note: 7 games in map, created 7 processes.
Ended process for game OW:[ 140377361861512, ]
Writing entry: CoD 2273 1136
Writing entry: DotA 2273 1136
Writing entry: GTAV 606 606
Writing entry: HotS 606 606
Writing entry: LoL 1470 735
Writing entry: MC 1050 525
Writing entry: OW 650759048 650759048
After processing: CoD:[ 1354,1442,]
After processing: DotA:[ 2137,1264,]
After processing: GTAV:[ 182,]
After processing: HotS:[ 2551,]
After processing: LoL:[ 606,1667,]
After processing: MC:[ 1277,193,]
After processing: OW:[ 857,]
Done!
多次运行程序会产生不同的结果,从某些游戏的正确结果到到处都是完全错误的数字。在程序完成后,我还列出了每个游戏的所有GameTime条目,以确保它没有被修改,以防这就是问题所在,但向量都毫发无损。
然而,从输出中可以看出,在同一个函数中从开始到结束(假设是常数和未修改的)迭代每次都会产生不同的结果。只有当任务并行运行时才会出现这种情况。如果顺序运行(在启动下一个future之前对每个future调用wait()),程序会正确运行,因此我的猜测是,由于某种原因,每个线程都在使其他线程的迭代器失效,即使它们是不同向量的输入迭代器,它们都是按值传递的。
我想知道是什么原因导致这种干扰,以及如何使它们并行工作
把const std::pair<GameName, std::vector<GameTime> >&
换成auto&&
或auto const&
,早上打电话给我。
你的代码每次循环都复制vector,因为你在map中存储的vector对的类型不完全正确。const&
可以绑定到临时类型,并且映射中的类型可以转换为您使用的类型。当const&
直接绑定到一个临时对象时,您将获得引用生存期扩展,并且该临时对象将一直持续到引用超出作用域为止。
转换同时复制GameName
和std::vector<GameTime>
。
const&
然后在循环结束时离开作用域,占用与它一起延长的临时it生命周期。std::vector<GameTime>
被摧毁。通常,它的内存会在下一次循环迭代时被重用。
如果你想知道,你的std::map<Blah>::value_type
是
std::pair<const GameName, std::vector<GameTime> >
,而上面的代码没有使第一个参数为const
。然而,这里的类型实际上并没有提供足够的信息,如果它们与映射中的类型不完全一致,代码将容易出现无声的错误,因此这是使用auto
的完美地方。
如果您确实想要转换为该类型,则仅在for(:)
循环中指定该类型。如果你想迭代副本,做for(auto x:y)
,如果在可变引用上做for(auto& x:y)
,如果你想声明你没有变异,做for(auto const& x:y)
或(更好,但c++ 17) for(auto&& x:std::as_const(y))
。
如果你真的不在乎,只是想提高效率,请执行for(auto&& x:y)
。
- CMake-按正确顺序将项目与C运行时对象文件链接
- 我在c++代码中生成了一个运行时#3异常
- 为什么在运行时没有向我们提供有关分段错误的更多信息?
- 删除指向指针的指针是运行时错误吗
- 如何用参数值调用函数(仅在运行时已知)
- 为什么即使使用-cudart-static进行编译,库用户仍然需要链接到cuda运行时
- 是否可以在编译时初始化数组,以便在运行时不会花费时间?
- c++中的指针和运行时错误
- 在运行时处理类型擦除的数据-如何不重新发明轮子
- 有没有一种方法可以测量c++程序的运行时内存使用情况
- 建议在运行时将带有类实例的列表从c++导入qml
- 无法理解此 return 语句的功能,没有它就会发生运行时错误
- 如何在GTK程序运行时禁用屏幕保护程序/电源管理/屏幕消隐
- 在同一模拟中使用静脉和静脉_ inet内容时出现运行时错误
- 读取文件时运行时的未知行为
- 函数在Windows或Linux上运行时表现不同
- 如果事件在仍在执行时再次设置,RegisterWaitForSingleObject 是否会并行运行回调?
- 接受迭代器对的函数在并行运行时不工作
- 使用OMP_SCHEDULE与#pragma omp并行调度(运行时)
- 在并行运行时使用 gcov 的代码覆盖率