我尝试从通过调用SerializeToString生成的二进制文件中解析许多Google协议缓冲区消息。我首先通过调用new函数将所有Bytes加载到堆内存中。我还有两个数组来存储堆内存中消息的字节开始地址和消息的字节数。 然后我开始通过调用ParseFromString来解析消息。我希望通过使用多线程来加快该过程。 在每个线程中,我传递地址数组的起始索引和结束索引以及字节计数数组。
在父进程中。主要代码是:
struct ParsePara
{
char* str_buffer;
size_t* buffer_offset;
size_t* binary_string_length_array;
size_t start_idx;
size_t end_idx;
Flight_Ticket_Info* ticket_info_buffer_array;
};
//Flight_Ticket_Info is class of message
//offset_size is the count of message
ticket_array = new Flight_Ticket_Info[offset_size];
const int max_thread_count = 6;
pthread_t pthread_id_vec[max_thread_count];
CTimer thread_cost;
thread_cost.start();
vector<ParsePara*> para_vec;
const size_t each_count = ceil(float(offset_size) / max_thread_count);
for (size_t k = 0;k < max_thread_count;k++)
{
size_t start_idx = each_count * k;
size_t end_idx = each_count * (k+1);
if (start_idx >= offset_size)
break;
if (end_idx >= offset_size)
end_idx = offset_size;
ParsePara* cand_para_ptr = new ParsePara();
if (!cand_para_ptr)
{
_ERROR_EXIT(0,"[Malloc memory fail.]");
}
cand_para_ptr->str_buffer = m_valdata;//heap memory for storing Bytes of message
cand_para_ptr->buffer_offset = offset_array;//begin address of each message
cand_para_ptr->start_idx = start_idx;
cand_para_ptr->end_idx = end_idx;
cand_para_ptr->ticket_info_buffer_array = ticket_array;//array to store message
cand_para_ptr->binary_string_length_array = binary_length_array;//Bytes count of each message
para_vec.push_back(cand_para_ptr);
}
for(size_t k = 0 ;k < para_vec.size();k++)
{
int ret = pthread_create(&pthread_id_vec[k],NULL,parserFlightTicketForMultiThread,para_vec[k]);
if (0 != ret)
{
_ERROR_EXIT(0,"[Error] [create thread fail]");
}
}
for (size_t k = 0;k < para_vec.size();k++)
{
pthread_join(pthread_id_vec[k],NULL);
}
在每个线程中,线程函数是:
void* parserFlightTicketForMultiThread(void* void_para_ptr)
{
ParsePara* para_ptr = (ParsePara*) void_para_ptr;
parserFlightTicketForMany(para_ptr->str_buffer,para_ptr->ticket_info_buffer_array,para_ptr->buffer_offset,
para_ptr->start_idx,para_ptr->end_idx,para_ptr->binary_string_length_array);
}
void parserFlightTicketForMany(const char* str_buffer,Flight_Ticket_Info* ticket_info_buffer_array,
size_t* buffer_offset,const size_t start_idx,const size_t end_idx,size_t* binary_string_length_array)
{
printf("start_idx:%d,end_idx:%d\n",start_idx,end_idx);
for (size_t k = start_idx;k < end_idx;k++)
{
if (k % 100000 == 0)
cout << k << endl;
size_t cand_offset = buffer_offset[k];
size_t binary_length = binary_string_length_array[k];
ticket_info_buffer_array[k].ParseFromString(string(&str_buffer[cand_offset],binary_length-1));
}
printf("done %ld %ld\n",start_idx,end_idx);
}
但是多线程成本不止一个线程。 一个线程的成本是:40455623ms 我的电脑是8芯,六线程成本是:131586865ms
任何人都可以帮助我吗?谢谢!
答案 0 :(得分:1)
一些可能的问题 - 您必须尝试确定哪个:
read()
成了一个大型数组还是mmap()
?在后一种情况下,数据是懒惰地从磁盘中读取的 - 在您实际尝试解析它之前它不会发生。即使在read()
情况下,也可能是数据已被换出,产生类似的效果。无论哪种方式,您的线程现在不只是争取内存带宽,而是磁盘带宽,当然 更慢。有六个线程读取大文件的不同部分肯定会比一个线程读取整个文件更慢,因为操作系统优化顺序访问。简而言之,如果您希望多个内核能够使代码更快,那么您不仅要考虑每个内核正在做什么,还要考虑每个内核的数据进出,以及内核需要多少内容对彼此。理想情况下,您希望每个核心都可以独立运行而无需与任何人或任何人交谈;然后你获得最大的并行度。当然,这通常是不可能的,但是你越接近那就越好。
BTW,随机优化:
ParseFromString(string(&str_buffer[cand_offset],binary_length-1))
将其替换为:
ParseFromArray(&str_buffer[cand_offset],binary_length-1)
在std::string
创建会复制数据,从而浪费时间(和内存带宽)。 (但这并不能解释为什么线程很慢。)