我不懂逻辑。我有两个元素计数相同的容器:vector
和unordered_map
。
我正在使用一个函数来检查某个函数的时间复杂度,并以毫秒为单位返回值
auto funcComplexity = [](auto&& func, auto&&... params)
{
const auto& start = high_resolution_clock::now();
for (auto i = 0; i < 100; ++i)
{
// function invocation using perfect forwarding
std::forward<decltype(func)>(func)(std::forward<decltype(params)>(params)...);
}
const auto& stop = high_resolution_clock::now();
return duration_cast<duration<double, std::milli>>(stop - start).count();
};
当我擦除vector
中间的元素时,实际上比擦除unordered_map
中的元素要少
void iterateUMap(std::unordered_map<int, Test> map)
{
map.erase(500);
}
void iterateVector(std::vector<Test> vector)
{
std::remove_if(vector.begin(), vector.end(), [](Test& val)
{
return val.mId == 500;
});
}
int main()
{
auto funcComplexity = [](auto&& func, auto&&... params)
{
const auto& start = high_resolution_clock::now();
for (auto i = 0; i < 10000; ++i)
{
// function invocation using perfect forwarding
std::forward<decltype(func)>(func)(std::forward<decltype(params)>(params)...);
}
const auto& stop = high_resolution_clock::now();
return duration_cast<duration<double, std::milli>>(stop - start).count();
};
std::unordered_map<int, Test> uMap;
for(int i = 0; i < 1000; i++)
{
uMap.try_emplace(i, Test(i));
}
std::vector<Test> vector;
vector.reserve(1000);
for (int i = 0; i < 1000; i++)
{
vector.emplace_back(i);
}
cout << funcComplexity(iterateUMap, uMap) << endl;
cout << endl;
cout << funcComplexity(iterateVector, vector) << endl;
return 0;
}
所以这两个函数的输出是:
对于向量为:52.6565毫秒
对于U_Map为:6740.64毫秒
为什么从u_map中删除实际上比从vector中删除要慢?当我只想得到element时也会发生同样的事情。例如:
void iterateUMap(std::unordered_map<int, Test> map)
{
map[500].DoSomething();
}
void iterateVector(std::vector<Test> vector)
{
for (auto& value : vector)
{
if(value.mId == 500)
{
value.DoSomething();
break;
}
}
}
1条答案
按热度按时间e5nszbig1#
你刚刚发现了现代计算机硬件的一个秘密。
现代的CPU非常非常擅长线性访问内存,比如向量中的数据是如何存储的,访问
X
线性内存地址比访问X
随机内存地址快几个数量级;并且在观察到这一点之前X
不必变得非常大。这是因为,一般来说,内存是以相对较大的块从RAM中检索的,然后存储在快速的CPU上的缓存中。大块内存块被缓存,访问下一个字节或字将命中快速的CPU上的缓存,而不会导致相对较慢的RAM访问。甚至在访问下一个字节或字之前,当CPU忙碌消化第一对字节时,CPU的不同部分将预先获取下一高速缓存行,从而预测未来的存储器访问。