0

我有一个多线程服务器应用程序。该应用程序从套接字接收数据,然后处理这些数据,如解包,添加到数据队列等,功能如下。该函数被频繁调用。有一个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(buffer)”,感谢您指出这一点,但我 100% 确定这不是问题,因为在我的应用程序中我不使用 sizeof(buffer),而是使用 ONE_MEGA (1024*1024) .

顺便说一句,尽管我不确定它是否有用,但还有一件事要提。将数组替换为较小的数组,例如 "char buffer[1024]; 也会显着降低 CPU 使用率。

[update3]
所有套接字都处于非阻塞模式。

4

3 回答 3

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是问题所在吗?

于 2013-07-29T10:54:17.373 回答
0

如果一切都是平等的,那么内存就是内存,你的缓冲区是在堆上还是在堆栈上都无关紧要。

但显然所有的事情都不是平等的。我怀疑堆栈 INTERFERES/OVERLAPS 上 1M 缓冲区的分配与分配给其他线程的堆栈空间。也就是说,要增加堆栈需要重新定位当前线程的堆栈,或者重新定位其他线程的堆栈。这需要时间。当从堆分配或堆栈分配足够小而不会干扰时,不需要这个时间,就像 1K 示例一样。

假设您正在使用与 Posix 兼容的线程实现,请查看

pthread_create
pthread_attr_getstack
pthread_attr_setstack

用于在线程创建时为具有 1M 缓冲区的线程提供更多堆栈空间。

-杰夫

于 2013-07-29T18:42:41.223 回答
-1

您忽略了recv. 这不好。部分读取是生活中的一个事实,如果您传递如此大的缓冲区,则很有可能。当您开始处理不包含有效数据的缓冲区部分时,可能会发生意想不到的事情。

最常用协议的最大帧大小为 64kB。甚至有可能(尽管不太可能)系统中的某些东西只使用缓冲区大小的最低 16 位,顺便说一下,您已将其设置为零。这将导致recv立即返回而不做任何事情,从而导致无限循环和高 CPU 使用率。

当然,这与动态分配的缓冲区没有什么不同,但如果您使用sizeof (buffer)并最终使用堆用户代码一次只读取一个指针大小的块,它可能会有所不同。

于 2013-07-30T00:28:20.050 回答