这是我的代码,我的unordered_map和map的行为相同,并且执行时间相同。我错过了一些关于这些数据结构的内容吗?
更新:我已根据以下答案和评论更改了我的代码。我删除了字符串操作以减少分析中的影响。现在我只测量find(),它占用了我代码中近40%的CPU。该配置文件显示unordered_map的速度提高了3倍,但有没有其他方法可以使这段代码更快?
#include <map>
#include <unordered_map>
#include <stdio.h>
struct Property {
int a;
};
int main() {
printf("Performance Summery:\n");
static const unsigned long num_iter = 999999;
std::unordered_map<int, Property > myumap;
for (int i = 0; i < 10000; i++) {
int ind = rand() % 1000;
Property p;
p.a = i;
myumap.insert(std::pair<int, Property> (ind, p));
}
clock_t tStart = clock();
for (int i = 0; i < num_iter; i++) {
int ind = rand() % 1000;
std::unordered_map<int, Property >::iterator itr = myumap.find(ind);
}
printf("Time taken unordered_map: %.2fs\n", (double)(clock() - tStart)/CLOCKS_PER_SEC);
std::map<int, Property > mymap;
for (int i = 0; i < 10000; i++) {
int ind = rand() % 1000;
Property p;
p.a = i;
mymap.insert(std::pair<int, Property> (ind, p));
}
tStart = clock();
for (int i = 0; i < num_iter; i++) {
int ind = rand() % 1000;
std::map<int, Property >::iterator itr = mymap.find(ind);
}
printf("Time taken map: %.2fs\n", (double)(clock() - tStart)/CLOCKS_PER_SEC);
}
输出在这里
Performance Summery:
Time taken unordered_map: 0.12s
Time taken map: 0.36s
答案 0 :(得分:6)
如果不进入您的代码,我会做一些一般性评论。
答案 1 :(得分:5)
我们有理由得到minimal, complete and verifiable个例子。这是我的代码:
#include <map>
#include <unordered_map>
#include <stdio.h>
struct Property {
int a;
};
static const unsigned long num_iter = 100000;
int main() {
printf("Performance Summery:\n");
clock_t tStart = clock();
std::unordered_map<int, Property> myumap;
for (int i = 0; i < num_iter; i++) {
int ind = rand() % 1000;
Property p;
//p.fileName = "hello" + to_string(i) + "world!";
p.a = i;
myumap.insert(std::pair<int, Property> (ind, p));
}
for (int i = 0; i < num_iter; i++) {
int ind = rand() % 1000;
myumap.find(ind);
}
printf("Time taken unordered_map: %.2fs\n", (double)(clock() - tStart)/CLOCKS_PER_SEC);
tStart = clock();
std::map<int, Property> mymap;
for (int i = 0; i < num_iter; i++) {
int ind = rand() % 1000;
Property p;
//p.fileName = "hello" + to_string(i) + "world!";
p.a = i;
mymap.insert(std::pair<int, Property> (ind, p));
}
for (int i = 0; i < num_iter; i++) {
int ind = rand() % 1000;
mymap.find(ind);
}
printf("Time taken map: %.2fs\n", (double)(clock() - tStart)/CLOCKS_PER_SEC);
}
运行时间是:
Performance Summery:
Time taken unordered_map: 0.04s
Time taken map: 0.07s
请注意,我运行的迭代次数是运行的10倍。
我怀疑你的版本有两个问题。首先,你运行的迭代太少,无法发挥作用。第二个是你在计数循环中进行昂贵的字符串操作。运行字符串操作所花费的时间大于使用无序映射所节省的时间,因此您没有看到性能上的差异。
答案 2 :(得分:2)
树(std::map
)或哈希映射(std::unordered_map
)是否更快取决于条目的数量和密钥的特征(值的可变性,比较和散列)功能等。)
但理论上,树比哈希映射慢,因为在二叉树内插入和搜索是 O(log2(N))在哈希映射中插入和搜索时的强>复杂性大致 O(1)复杂性。
您的测试没有显示,因为:
您在循环中调用rand()
。与地图插入相比,这需要很长时间。它会为您正在测试的两个地图生成不同的值,甚至会进一步扭曲结果。使用重量较轻的发电机,例如minstd
LCG。
您需要更高分辨率的时钟和更多迭代,以便每次测试运行至少需要几百毫秒。
您需要确保编译器不重新排序您的代码,以便定时调用发生在应有的位置。这并不总是那么容易。定时测试周围的记忆围栏通常有助于解决这个问题。
由于你没有使用他们的值,你find()
次调用很有可能被优化掉(我碰巧知道至少GCC在-O2模式下没有这样做,所以我保持原样。)
字符串连接相比也很慢。
这是我的更新版本:
#include <atomic>
#include <chrono>
#include <iostream>
#include <map>
#include <random>
#include <string>
#include <unordered_map>
using namespace std;
using namespace std::chrono;
struct Property {
string fileName;
};
const int nIter = 1000000;
template<typename MAP_TYPE>
long testMap() {
std::minstd_rand rnd(12345);
std::uniform_int_distribution<int> testDist(0, 1000);
auto tm1 = high_resolution_clock::now();
atomic_thread_fence(memory_order_seq_cst);
MAP_TYPE mymap;
for (int i = 0; i < nIter; i++) {
int ind = testDist(rnd);
Property p;
p.fileName = "hello" + to_string(i) + "world!";
mymap.insert(pair<int, Property>(ind, p));
}
atomic_thread_fence(memory_order_seq_cst);
for (int i = 0; i < nIter; i++) {
int ind = testDist(rnd);
mymap.find(ind);
}
atomic_thread_fence(memory_order_seq_cst);
auto tm2 = high_resolution_clock::now();
return (long)duration_cast<milliseconds>(tm2 - tm1).count();
}
int main()
{
printf("Performance Summary:\n");
printf("Time taken unordered_map: %ldms\n", testMap<unordered_map<int, Property>>());
printf("Time taken map: %ldms\n", testMap<map<int, Property>>());
}
Compiled with -O2
,它给出了以下结果:
Performance Summary:
Time taken unordered_map: 348ms
Time taken map: 450ms
因此在中使用unordered_map
此特定情况的速度提高约20-25%。
答案 3 :(得分:1)
使用unordered_map,查找不仅速度更快。这个稍微修改过的测试也会比较填充时间。
我做了一些修改:
-
#include <map>
#include <unordered_map>
#include <vector>
#include <stdio.h>
struct Property {
int a;
};
struct make_property : std::vector<int>::const_iterator
{
using base_class = std::vector<int>::const_iterator;
using value_type = std::pair<const base_class::value_type, Property>;
using base_class::base_class;
decltype(auto) get() const {
return base_class::operator*();
}
value_type operator*() const
{
return std::pair<const int, Property>(get(), Property());
}
};
int main() {
printf("Performance Summary:\n");
static const unsigned long num_iter = 9999999;
std::vector<int> keys;
keys.reserve(num_iter);
std::generate_n(std::back_inserter(keys), num_iter, [](){ return rand() / 10000; });
auto time = [](const char* message, auto&& func)
{
clock_t tStart = clock();
func();
clock_t tEnd = clock();
printf("%s: %.2gs\n", message, double(tEnd - tStart) / CLOCKS_PER_SEC);
};
std::unordered_map<int, Property > myumap;
time("fill unordered map", [&]
{
myumap.insert (make_property(keys.cbegin()),
make_property(keys.cend()));
});
std::map<int, Property > mymap;
time("fill ordered map",[&]
{
mymap.insert(make_property(keys.cbegin()),
make_property(keys.cend()));
});
time("find in unordered map",[&]
{
for (auto k : keys) { myumap.find(k); }
});
time("find in ordered map", [&]
{
for (auto k : keys) { mymap.find(k); }
});
}
示例输出:
Performance Summary:
fill unordered map: 3.5s
fill ordered map: 7.1s
find in unordered map: 1.7s
find in ordered map: 5s