【发布时间】:2021-02-03 16:07:08
【问题描述】:
所以问题来了:我有一个自定义对象的向量,我需要用向量中每个唯一的对象组合计算一些东西,如果结果是某个值,我需要构建一个 2-way关联列表。这是我按顺序完成的工作:
#include <itertools/combinations.hpp> //I'm using cppitertools library (installed through vcpkg)
class MyClass{
private:
int x;
std::string abc;
public:
MyClass();
GetX();
}
...
std::vector<MyClass> _myObjects;
std::unordered_map<const MyClass*, std::vector<const MyClass*>> _relationships;//list all other objects in relation with a specific object, based on custom computation on all possible combination
...
int ComputeRelation(const MyObject* obj0, const MyObject* obj1){
//some random stuff
return obj0->GetX() + obj1->GetX();
}
...
void BuildRelationship(){
for(auto& combination : iter::Combination(_myObject,2)){
//combination is a pair of MyObject
if(ComputeRelation(combination[0],combination[1]) == 1){
//build 2-way associative list
_relationships[combination[0]].push_back(combination[1]);
_relationships[combination[1]].push_back(combination[0]);
}
}
}
我最初到处都有引用而不是指针,但是 unordered_map 在使用引用时会抛出错误。
现在,这很好,但我想通过并行化循环来加速它,因为组合的数量会很快变得非常大(对于 100 万个对象的数据集,5000 亿个组合)
这是我尝试过的:
- std::for_each 与并行执行策略:我收到
Parallel algorithms require forward iterators or stronger错误 --> 我无法控制迭代器类型,因为我是从 cppitertools 库中获取的 - taskflow.for_each(来自 cpptaskflow 库):结果不一致。一开始我以为是因为并发访问
_relationships,所以我尝试了互斥体但结果相同,所以我认为这又与迭代器类型有关。 - 也尝试了变换/减少,但我遇到了与 std::for_each 相同的问题
我没有找到一种方法来简单地拆分组合集,而不必先将所有组合推入另一个容器(我认为这是低效的)
我觉得我错过了一些明显、简单的方法,但我的大脑有点卡住了。
谢谢!
编辑:
我最初只想保留一张包含 ComputeRelation 的所有结果的地图,但是由于有 5000 亿个元素,我的 RAM 使用量达到了疯狂的水平(>>100G),这就是为什么我决定只保留具有特定价值的关系感兴趣的
更新#1: 所以我想我找到了解决方法。我向 MyClass 添加了一个成员属性,用作 ID 或键。我使用了一个简单的散列库来散列我的类的一些属性(将字符串转换为 int)。然后,我没有构建指针容器,而是构建 int 容器,并在需要时使用哈希值从映射中检索实际对象,如下所示:
#include <itertools/combinations.hpp> //I'm using cppitertools library (installed through vcpkg)
class MyClass{
private:
int x;
std::string abc;
uint64_t hash;
public:
MyClass(int x, string abc){
this.hash=SomeHashFn(abc);
};
GetX();
}
...
std::vector<uint64_t> _myHashes;//keeps order and duplicates
std::unordered_map<uint64_t, MyObject> _myObjects;
std::unordered_map<uint64_t, std::vector<uint64_t>> _relationships;//list all other objects in relation with a specific object, based on custom computation on all possible combinations
...
int ComputeRelation(const MyObject& obj0, const MyObject& obj1){
//some random stuff
return obj0.GetX() + obj1.GetX();
}
...
void BuildRelationship(){
auto combinations = iter::Combination(_myHashes,2);
std::for_each(std::execution::par, combinations.begin(), combinations.end(), [&](auto combination){
//combination is a pair of hash
MyObject obj0=_myObjects[combination[0]];
MyObject obj1=_myObjects[combination[1]];
if(ComputeRelation(obj0, obj1) == 1){
//build 2-way associative list using hashes
_relationships[combination[0]].push_back(combination[1]);
_relationships[combination[1]].push_back(combination[0]);
}
}
}
更新 #2:
没关系 Update#1,由于iter::Combination 返回的迭代器,我仍然无法并行处理组合(对于std::for_each,我需要向前或更强)。所以我尝试创建一个包含所有组合的向量,但又遇到了 RAM 问题。快速计算告诉我,我需要大约 80Gb 来存储这个向量。
【问题讨论】:
-
你真的需要
_relationships在一张巨大的地图上吗?之后你会用它做什么? -
我使用地图来检查对象 X 是否与 Y 有关系(很多时间)并找到 n 度关系(如遍历图表,两个对象之间所需的步长)
标签: c++ multithreading iterator combinations