我正在尝试优化需要很长时间的C ++代码的某些部分(对于X数据量,代码的以下部分需要大约19秒,并且我试图在少于5个时间内完成整个过程相同数量的数据的秒数 - 基于我的一些基准测试。我有一个函数“add”,我已经编写并复制了代码。我将尝试尽可能多地解释我认为需要理解代码。如果我错过了什么,请告诉我。
对于X数据条目,以下函数add被称为X次。
void HashTable::add(PointObject vector) // PointObject is a user-defined object
{
int combinedHash = hash(vector); // the function "hash" takes less than 1 second for X amount of data
// hashTableMap is an unordered_map<int, std::vector<PointObject>>
if (hashTableMap.count(combinedHash) == 0)
{
// if the hashmap does not contain the combinedHash key, then
// add the key and a new vector
std::vector<PointObject> pointVectorList;
pointVectorList.push_back(vector);
hashTableMap.insert(std::make_pair(combinedHash, pointVectorList));
}
else
{
// otherwise find the key and the corresponding vector of PointObjects and add the current PointObject to the existing vector
auto it = hashTableMap.find(combinedHash);
if (it != hashTableMap.end())
{
std::vector<PointObject> pointVectorList = it->second;
pointVectorList.push_back(vector);
it->second = pointVectorList;
}
}
}
答案 0 :(得分:19)
你正在做很多无用的操作......如果我理解正确,简化形式可能只是:
void HashTable::add(const PointObject& vector) {
hashTableMap[hash(vector)].push_back(vector);
}
这是因为
operator[]
访问地图时,如果地图中尚未显示默认初始值,则会创建地图std::vector
)由引用返回,因此您可以直接push_back
传入的点。如果密钥已经在地图中,则此std::vector
将是新插入的密码或先前存在的密码。另请注意,根据PointObject
的大小和其他因素,按值传递vector
而不是const PointObject&
可能更有效。这是一种微观优化,但需要进行合理的分析。
答案 1 :(得分:5)
最好只插入新元素并检查insert()
返回的内容,而不是调用hashTableMap.count(combinedHash)
和hashTableMap.find(combinedHash)
:
在版本(1)和(2)中,该函数返回一个对象 第一个元素是一个迭代器,指向新插入的 容器中的元素或其键等效的元素, 以及表示元素是否成功的bool值 插入与否。
此外,不要按价值传递对象,而不是必须的。最好通过指针或引用传递它。这样:
std::vector<PointObject> pointVectorList = it->second;
是低效的,因为它会创建一个不必要的向量副本。
答案 2 :(得分:2)
如果没有if
,请尝试在哈希表上插入一个空条目:
auto ret = hashTableMap.insert(
std::make_pair(combinedHash, std::vector<PointObject>());
将添加新的空白条目,或者将检索已存在的条目。在您的情况下,您不需要检查它是什么情况,您只需要获取返回的迭代器并添加新元素:
auto &pointVectorList = *ret.first;
pointVectorList.push_back(vector);
答案 3 :(得分:2)
此.count()
完全不必要,您可以将功能简化为:
void HashTable::add(PointObject vector)
{
int combinedHash = hash(vector);
auto it = hashTableMap.find(combinedHash);
if (it != hashTableMap.end())
{
std::vector<PointObject> pointVectorList = it->second;
pointVectorList.push_back(vector);
it->second = pointVectorList;
}
else
{
std::vector<PointObject> pointVectorList;
pointVectorList.push_back(vector);
hashTableMap.insert(std::make_pair(combinedHash, pointVectorList));
}
}
您还在各处执行复制操作。复制对象非常耗时,避免这样做。在可能的情况下也使用引用和指针:
void HashTable::add(PointObject& vector)
{
int combinedHash = hash(vector);
auto it = hashTableMap.find(combinedHash);
if (it != hashTableMap.end())
{
it->second.push_back(vector);
}
else
{
std::vector<PointObject> pointVectorList;
pointVectorList.push_back(vector);
hashTableMap.insert(std::make_pair(combinedHash, pointVectorList));
}
}
此代码可能会进一步优化,但需要了解hash()
,知道hashTableMap
的工作方式(顺便说一句,为什么它不是std::map
?)和一些实验
如果hashTableMap
是std::map<int, std::vector<pointVectorList>>
,您可以将功能简化为:
void HashTable::add(PointObject& vector)
{
hashTableMap[hash(vector)].push_back(vector);
}
如果它是std::map<int, std::vector<pointVectorList*>>
(指针),你甚至可以避免最后一次复制操作。
答案 4 :(得分:1)
你的最大问题是你在else部分中复制整个矢量(以及该矢量中的每个元素)两次:
std::vector<PointObject> pointVectorList = it->second; // first copy
pointVectorList.push_back(vector);
it->second = pointVectorList; // second copy
这意味着每次向现有向量添加元素时,都会复制整个向量。
如果您使用了对该向量的引用,那么您可以做得更好:
std::vector<PointObject> &pointVectorList = it->second;
pointVectorList.push_back(vector);
//it->second = pointVectorList; // don't need this anymore.
在旁注中,在unordered_map
中,您正在将您的价值作为关键。
您可以使用带有哈希函数的unordered_set
。
答案 5 :(得分:1)
使用std::unordered_map
似乎不合适 - 您使用int
中的hash
作为密钥(可能)是PointObject
的散列而不是{{ 1}}本身。基本上是双重哈希。而且如果你需要一个PointObject
来计算地图密钥,那么它根本就不是一个密钥!也许PointObject
可能是更好的选择?
首先定义哈希函数形式std::unordered_multiset
PointObject
然后像
namespace std
{
template<>
struct hash<PointObject> {
size_t operator()(const PointObject& p) const {
return ::hash(p);
}
};
}
答案 6 :(得分:1)
假设PointObject
很大并且制作副本很贵,std::move
就是你的朋友。您需要确保PointObject
是移动感知的(要么不定义析构函数或复制运算符,要么自己提供移动构造函数和移动赋值运算符)。
void HashTable::add(PointObject vector) // PointObject is a user-defined object
{
int combinedHash = hash(vector); // the function "hash" takes less than 1 second for X amount of data
// hashTableMap is an unordered_map<int, std::vector<PointObject>>
if (hashTableMap.count(combinedHash) == 0)
{
// if the hashmap does not contain the combinedHash key, then
// add the key and a new vector
std::vector<PointObject> pointVectorList;
pointVectorList.push_back(std::move(vector));
hashTableMap.insert(std::make_pair(combinedHash, std::move(pointVectorList)));
}
else
{
// otherwise find the key and the corresponding vector of PointObjects and add the current PointObject to the existing vector
auto it = hashTableMap.find(combinedHash);
if (it != hashTableMap.end())
{
std::vector<PointObject> pointVectorList = it->second;
pointVectorList.push_back(std::move(vector));
it->second = std::move(pointVectorList);
}
}
}