我有一个多线程服务器应用程序。此应用程序从套接字接收数据然后处理这些数据,如解包包,添加到数据队列等,功能如下。经常调用此函数。有一个select语句,如果发现有数据,它将调用此函数来接收):
//the main function used to receive
//file data from clients
void service(void){
while(1){
....
struct timeval timeout;
timeout.tv_sec = 3;
...
ret = select(maxFd+1, &read_set, NULL, NULL, &timeout);
if (ret > 0){
//get socket from SocketsMap
//if fd in SocketsMap and its being set
//then receive data from the socket
receive_data(fd);
}
}
}
void receive_data(int fd){
const int ONE_MEGA = 1024 * 1024;
//char *buffer = new char[ONE_MEGA]; consumes much less CPU
char buffer[ONE_MEGA]; // cause high CPU
int readn = recv(fd, buffer, ONE_MEGA, 0);
//handle the data
}
我发现上面消耗了太多的CPU - 通常是80%到90%,但是如果我从堆创建缓冲区而CPU只有14%。为什么呢?
[更新]
添加了更多代码
[UPDATE2]
最令人遗憾的是,我还写了另一个简单的数据接收服务器和客户端。服务器只是从套接字接收数据然后丢弃它。两种类型的空间分配几乎相同,CPU使用率没有太大差异。在有问题的多线程服务器应用程序中,我甚至将进程堆栈大小重置为30M,使用数组仍会导致问题,但是从堆分配解决了它。我不知道为什么。
关于“sizeof(缓冲区)”,感谢指出这一点,但我100%确定它不是问题,因为在我的应用程序中我不使用sizeof(缓冲区),而是ONE_MEGA(1024 * 1024)而是。
顺便说一下,还有一件事需要提及,但我不确定它是否有用。 用较小的数组替换数组,例如“char buffer [1024];也会显着降低cpu的使用率。
[UPDATE3]
所有套接字都处于非阻塞模式。
答案 0 :(得分:3)
我刚刚写了这个:
#include <iostream>
#include <cstdio>
using namespace std;
static __inline__ unsigned long long rdtsc(void)
{
unsigned hi, lo;
__asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi));
return ( (unsigned long long)lo)|( ((unsigned long long)hi)<<32 );
}
const int M = 1024*1024;
void bigstack()
{
FILE *f = fopen("test.txt", "r");
unsigned long long time;
char buffer[M];
time = rdtsc();
fread(buffer, M, 1, f);
time = rdtsc() - time;
fclose(f);
cout << "bs: Time = " << time / 1000 << endl;
}
void bigheap()
{
FILE *f = fopen("test.txt", "r");
unsigned long long time;
char *buffer = new char[M];
time = rdtsc();
fread(buffer, M, 1, f);
time = rdtsc() - time;
delete [] buffer;
fclose(f);
cout << "bh: Time = " << time / 1000 << endl;
}
int main()
{
for(int i = 0; i < 10; i++)
{
bigstack();
bigheap();
}
}
输出是这样的:
bs: Time = 8434
bh: Time = 7242
bs: Time = 1094
bh: Time = 2060
bs: Time = 842
bh: Time = 830
bs: Time = 785
bh: Time = 781
bs: Time = 782
bh: Time = 804
bs: Time = 782
bh: Time = 778
bs: Time = 792
bh: Time = 809
bs: Time = 785
bh: Time = 786
bs: Time = 782
bh: Time = 829
bs: Time = 786
bh: Time = 781
换句话说,从堆栈堆栈中分配绝对没有区别。开始时的少量“缓慢”与“加热缓存”有关。
而且我相信你的代码在两者之间表现不同的原因是另一回事 - 也许simonc说:sizeof buffer
是问题?
答案 1 :(得分:0)
如果所有事情都是平等的,那么内存就是内存,无论你的缓冲区是在堆上还是在堆栈上都无关紧要。
但显然一切都不平等。我怀疑在堆栈INTERFERES / OVERLAPS上分配1M缓冲区,并将堆栈空间分配给OTHER线程。也就是说,增加堆栈需要重新定位当前线程的堆栈,或重新定位其他线程的堆栈。这需要时间。从堆中分配时,或者如果堆栈分配小到不干扰,则不需要此时间,就像1K示例一样。
假设您正在使用与Posix兼容的线程实现,请查看
pthread_create
pthread_attr_getstack
pthread_attr_setstack
在线程创建时为线程提供1M缓冲区更多的堆栈空间。
-Jeff
答案 2 :(得分:-1)
您忽略了recv
的返回值。这不好。部分读取是生活中的事实,如果你传递这么大的缓冲区很可能。当您开始处理不包含有效数据的缓冲区部分时,可能会发生意外情况。
最常用协议的最大帧大小为64kB。甚至可能(尽管不太可能)系统中的某些东西只使用缓冲区大小的最低16位,顺便提一下,你已经设置为零。哪会导致recv
立即返回而不做任何事情,导致无限循环和高CPU使用率。
当然,对于动态分配的缓冲区,这一切都不应该有任何不同,但是如果你也使用了sizeof (buffer)
并最终得到只读指针的堆用户代码 - 一次大小,可能会有所不同。