最近遇到日志队列记录每个客户端ID传送过来的日志,里面的数据量,多的时候非常庞大,从服务器再传到网页效率偶尔感觉较低,故从数据结构和网页Http协议上做了优化
里面最开始有个结构体,std::map<std::string clientID,logStruct logInfo>
用于存储每个客户端的总日志信息。这个Map里面存了大量的日志路径和其他的参数项
考虑优化该map
C++ STL Map模板类中除了 insert() 方法 还提供了 emplace() 和 emplace_hint() 成员函数,也可以实现向 map 容器中插入新的键值对。
实现相同的插入操作,都比 insert() 方法的效率高
关于这两个函数,做了一些验证
得出的结论是
当调用insert时,是将对象传递给insert,对象被拷贝到容器中,
而当我们使用emplace时,是将参数传递给构造函,emplace使用这些参数在容器管理的内存空间中直接构造元素
map容器的三种插入方式insert emplace emplace_hint效率对比,测试平台为X64 release
#include <iostream>
#include <map>
#include <string>
class testMapDemo
{
public:
testMapDemo(int num) : num(num)
{
std::cout << "testMapDemo构造函数" << std::endl;
}
testMapDemo(const testMapDemo& other) : num(other.num)
{
std::cout << "testMapDemo拷贝构造函数" << std::endl;
}
testMapDemo(testMapDemo&& other) : num(other.num)
{
std::cout << "testMapDemo移动构造函数" << std::endl;
}
private:
int num;
};
int main()
{
//创建空 map 容器
std::map<std::string, testMapDemo>mymap;
std::cout << "调用insert:" << std::endl;
mymap.insert({ "insert", testMapDemo(100) });
//
std::cout << "调用emplace:" << std::endl;
mymap.emplace("emplace", 100);
//
std::cout << "调用emplace_hint:" << std::endl;
mymap.emplace_hint(mymap.begin(), "emplace_hint", 100);
return 0;
}
ecplace和emplace都是在内部构造。少了两次移动构造。都说移动构造可以忽略不记,比拷贝代价低很多,那我们修改下上面的代码,循环插入10万次 和100万次看下。
#include <iostream>
#include <map>
#include <string>
#include<time.h>
class testMapDemo
{
public:
testMapDemo(int num) : num(num)
{
//std::cout << "testMapDemo构造函数" << std::endl;
}
testMapDemo(const testMapDemo& other) : num(other.num)
{
// std::cout << "testMapDemo拷贝构造函数" << std::endl;
}
testMapDemo(testMapDemo&& other) : num(other.num)
{
//std::cout << "testMapDemo移动构造函数" << std::endl;
}
private:
int num;
};
int main()
{
int testTimes = 1000000;
//计时
clock_t start, end;
start = clock();
//创建空 map 容器
std::map<std::string, testMapDemo>mymap_insert;
for (int i = 0; i < testTimes; i++)
{
//模拟不同key插入
std::string tempKey = "insert" + std::to_string(i);
mymap_insert.insert({ tempKey, testMapDemo(100) });
}
end = clock();
double insertTime = (double)(end - start);
std::cout << "调用insert 100万次,时间:" << insertTime / CLOCKS_PER_SEC << std::endl;
//
//创建空 mymap_emplace 容器
start = clock();
std::map<std::string, testMapDemo>mymap_emplace;
for (int i = 0; i < testTimes; i++)
{
//模拟不同key插入
std::string tempKey = "insert" + std::to_string(i);
mymap_emplace.emplace(tempKey, 100);
}
end = clock();
insertTime = (double)(end - start);
std::cout << "调用emplace 100万次,时间:" << insertTime / CLOCKS_PER_SEC << std::endl;
//
//创建空 mymap_emplace_hint 容器
start = clock();
std::map<std::string, testMapDemo>mymap_emplace_hint;
for (int i = 0; i < testTimes; i++)
{
//模拟不同key插入
std::string tempKey = "insert" + std::to_string(i);
mymap_emplace_hint.emplace(tempKey, 100);
}
end = clock();
insertTime = (double)(end - start) ;
std::cout << "调用emplace_hint 100万次,时间:" << insertTime / CLOCKS_PER_SEC << std::endl;
return 0;
}
因为emplace_hint是可以指定位置插入的。会改变数据原有的位置和调整,所以有时候时间可能因为内部调整会慢一些,但是比insert效率还是来的高
写在结尾:
值得一提的是,一开始踩过的坑
std::map的不管哪个方法会忽略重复key,而不是替换,std::map必须先找出来erase掉才能插入。
QT里的QMap会默认直接替换。