1 只简单测试加锁和不加锁的开销:
#include <map>
#include <mutex>
#include <chrono>
#include <string>
#include <time.h>
#include <thread>
#include <unistd.h>
using namespace std;
const string val = "val";
map<int, string> testmap;
mutex io_mutex;
void testDireAddLock(int size, bool islock){
clock_t start = clock();
for (int i = 0; i < size; ++i)
{
if (islock){
lock_guard<mutex> lk(io_mutex);
testmap.insert(std::make_pair(i, val));
}
else{
testmap.insert(std::make_pair(i, val));
}
}
printf("user:%lld\n", clock()-start);
}
int main(int agrc, char** argv){
for (int i = 0; i < atoi(argv[1]); ++i)
{
thread workthread = thread(&testDireAddLock, atoi(argv[2]), atoi(argv[3]));
workthread.join();
}
//sleep(100);
}
当然如果不加锁 肯定会有数据争用.
这里只是为了简单测试:
allen@allenPC:~/code/c++code$ ./testtime 2 100 1
user:183
user:87
allen@allenPC:~/code/c++code$ ./testtime 2 100 0
user:135
user:60
加锁开销还是挺大的.
2. 然后进行了初步改进
#include <map>
#include <mutex>
#include <chrono>
#include <string>
#include <time.h>
#include <thread>
#include <unistd.h>
#include <queue>
#include <condition_variable>
#include <string.h>
#include <vector>
using namespace std;
const string val = "val";
class Buffer{
public:
Buffer(int length):length(length), pData(new char[length]), m_golen(0), m_getint(0){}
~Buffer(){
if(pData){
delete []pData;
}
}
void addint(int val){
memcpy(pData+m_golen, &val, sizeof(val));
m_golen += sizeof(val);
}
void addstr(const string& str){
addint(str.size());
memcpy(pData+m_golen, str.data(), str.size());
m_golen += str.size();
}
void getint(int& val){
memcpy((void*)&val, pData + m_getint, sizeof(int));
m_getint += sizeof(int);
}
void getstr(string& str){
int len = 0;
getint(len);
str = string(pData + m_getint, len);
m_getint += len;
}
private:
int m_getint;
int m_golen;
char* pData;
int length;
};
volatile unsigned int usedtime = 0;
class Actor{
public:
Actor(){
iothread = thread(&Actor::Run, this);
}
void join(){
iothread.join();
}
void Send(Buffer* pBuffer){
if(pBuffer){
lock_guard<mutex> lk(io_mutex);
bool ifneedNotify = m_bufferQueue.empty();
m_bufferQueue.push(pBuffer);
m_condition.notify_one();
}
}
void Run(){
while(true){
std::unique_lock <std::mutex> lck(io_mutex);
if(m_bufferQueue.empty()){
m_condition.wait(lck);
}
std::queue<Buffer*> tmpQueue;
tmpQueue.swap(m_bufferQueue);
lck.unlock();
while(!tmpQueue.empty()){
Buffer* pBuffer = tmpQueue.front();
int key = 0;
pBuffer->getint(key);
string val;
pBuffer->getstr(val);
testmap.insert(make_pair(key, val));
printf("actor:key:%d, val:%s\n", key, val.c_str());
delete pBuffer;
tmpQueue.pop();
}
}
}
private:
thread iothread;
mutex io_mutex;
map<int, string> testmap;
queue<Buffer*> m_bufferQueue;
condition_variable m_condition;
};
Actor gActor;
void testDireAddLock(int size){
const string test = "just for fun!";
for (int i = 0; i < size; ++i)
{
Buffer* pBuffer = new Buffer(100);
pBuffer->addint(i);
pBuffer->addstr(test);
printf("workthread send %s\n", test.c_str() );
gActor.Send(pBuffer);
}
}
int main(int agrc, char** argv){
std::vector<thread> threadVec;
for (int i = 0; i < atoi(argv[1]); ++i)
{
threadVec.push_back(thread(&testDireAddLock, atoi(argv[2])));
}
gActor.join();
for (int i = 0; i < threadVec.size(); ++i)
{
threadVec[i].join();
}
sleep(10000);
}
开多个工作线程,在actor进行处理,这样减小了锁的粒度.
但是结果是:
usedtime:5430
居然第一种直接加锁更快.我觉得开销应该在频繁的new delete上.初步分析是这样.
3.进一步进行优化,用内存池提前分配内存,避免反复new/delete的开销,利用tbb并行库实现一个线程安全的buffer内存池,具体实现如下:
#include <map>
#include <mutex>
#include <chrono>
#include <string>
#include <time.h>
#include <thread>
#include <unistd.h>
#include <queue>
#include <condition_variable>
#include <string.h>
#include <vector>
#include <tbb/tbb.h>
#include <tbb/parallel_for.h>
using namespace std;
using namespace tbb;
int allnum = 0;
class Buffer{
public:
Buffer(int length):length(length), pData(new char[length]), m_golen(0), m_getint(0){}
~Buffer(){
if(pData){
delete []pData;
}
}
void addint(int val){
memcpy(pData+m_golen, &val, sizeof(val));
m_golen += sizeof(val);
}
void addstr(const string& str){
addint(str.size());
memcpy(pData+m_golen, str.data(), str.size());
m_golen += str.size();
}
void getint(int& val){
memcpy((void*)&val, pData + m_getint, sizeof(int));
m_getint += sizeof(int);
}
void getstr(string& str){
int len = 0;
getint(len);
str = string(pData + m_getint, len);
m_getint += len;
}
private:
int m_getint;
int m_golen;
char* pData;
int length;
};
concurrent_queue<Buffer*> gBufferQueue;
void init(){
for (int i = 0; i < 1024; ++i)
{
Buffer* pBuffer = new Buffer(100);
gBufferQueue.push(pBuffer);
}
}
volatile unsigned int usedtime = 0;
class Actor{
public:
Actor(){
iothread = thread(&Actor::Run, this);
handelnum = 0;
}
void join(){
iothread.join();
}
void Send(Buffer* pBuffer){
if(pBuffer){
lock_guard<mutex> lk(io_mutex);
bool ifneedNotify = m_bufferQueue.empty();
m_bufferQueue.push(pBuffer);
m_condition.notify_one();
}
}
void Run(){
while(true){
std::unique_lock <std::mutex> lck(io_mutex);
if(m_bufferQueue.empty()){
m_condition.wait(lck);
}
std::queue<Buffer*> tmpQueue;
tmpQueue.swap(m_bufferQueue);
lck.unlock();
while(!tmpQueue.empty()){
Buffer* pBuffer = tmpQueue.front();
int key = 0;
pBuffer->getint(key);
string val;
pBuffer->getstr(val);
testmap.insert(make_pair(key, val));
//printf("actor:key:%d, val:%s\n", key, val.c_str());
gBufferQueue.push(pBuffer);
tmpQueue.pop();
++handelnum;
}
if(handelnum == allnum){
printf("usedtime:%d\n", clock() - usedtime);
}
}
}
private:
thread iothread;
std::mutex io_mutex;
map<int, string> testmap;
queue<Buffer*> m_bufferQueue;
condition_variable m_condition;
int handelnum;
};
Actor* gActor = NULL;
void testDireAddLock(int size){
const string test = "just for fun!";
for (int i = 0; i < size; ++i)
{
Buffer* pBuffer = NULL;
gBufferQueue.try_pop(pBuffer);
pBuffer->addint(i);
pBuffer->addstr(test);
gActor->Send(pBuffer);
}
}
int main(int agrc, char** argv){
std::vector<thread> threadVec;
int threadnum = atoi(argv[1]);
int testsize = atoi(argv[2]);
allnum = testsize * threadnum;
<span style="white-space:pre"> </span>init();
usedtime = clock();
gActor = new Actor();
for (int i = 0; i < threadnum; ++i)
{
threadVec.push_back(thread(&testDireAddLock, testsize));
}
gActor->join();
for (int i = 0; i < threadVec.size(); ++i)
{
threadVec[i].join();
}
delete gActor;
sleep(10000);
}
allen@allenPC:~/code/c++code$ ./test 2 100
usedtime:1354
开销一下子降下来了,但是还是没有直接加锁的快.