c++ - 如何使用并行性来提高我的代码的性能?(尝试了一些东西)
问题描述
所以问题来了:我有一个自定义对象的向量,我需要用向量中每个唯一的对象组合计算一些东西,如果结果是某个值,我需要构建一个 2 路关联列表。这是我按顺序执行的操作,并且有效:
#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:没关系更新#1,由于iter::Combination
返回的迭代器(我需要向前或更强std::for_each
),我仍然无法并行处理组合。所以我尝试创建一个包含所有组合的向量,但又遇到了 RAM 问题。一个快速的计算告诉我,我需要大约 80Gb 来存储这个向量。
解决方案
std::unordered_map
可能非常慢(在今天可用的大多数实现中)。尝试另一个容器,例如dense_hash_map
.您可以通过键的某些属性“分片”结果映射。例如,给定一个类型为 的键
MyClass*
,您可以散列指针值并使用hash % n
,其中n
是分片的数量,作为子映射向量的索引。然后,您将能够在并行线程中填充n
子映射(理想情况下= 可用处理器的数量)。 缺点是查找变得有点复杂 - 您还需要在查找期间进行计算。您可以使用散列函数来找到性能和均匀性之间的最佳平衡。例如,可能是一个足够好但非常快的哈希函数。n
n
hash % n
(uintptr_t)key >> 4
推荐阅读
- python - 当我增加框架厚度时,信号器消失
- django-models - 上传多个文件 (POST) __init__() 接受 1 个位置参数,但给出了 2 个
- spring - 春季开发工具故障排除
- java - 创建一个对象以在多个 TestNG 类之间共享数据
- cloud-foundry - 将文件夹复制到 Pivotal Cloud Foundry
- asp.net-core - 如何在 asp.net 核心中将 Map 插入到 dynamodb 表
- c# - 调用 EntityTypeBuilder
.HasKey 通过反射获得复合键 - java - java.util.zip.ZipException: 无效代码 -- 缺少块尾 --> 在调用 ZipInputStream.closeEntry() 时
- apache-kafka - kafka 中的 b/w group.id、application.id 和 client.id 有什么区别?
- c - 如何在c中写入绝对值