1mega大小的大阵列导致CPU过高

Big array of size 1mega caused high CPU?

本文关键字:CPU 过高 阵列 1mega      更新时间:2023-10-16

我有一个多线程服务器应用程序。该应用程序从套接字接收数据,然后处理这些数据,如拆包、添加到数据队列等,功能如下。此函数经常被调用。有一个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%。为什么?

[更新]
添加了更多代码

[更新2]
最重要的是,我还编写了另一个简单的数据接收服务器和客户端。服务器只是从套接字接收数据,然后丢弃它。两种类型的空间分配工作原理几乎相同,CPU使用率没有太大差异。在出现问题的多线程服务器应用程序中,我甚至将进程堆栈大小重置为30M,使用数组仍然会出现问题,但从堆中分配解决了问题。我不知道为什么。

关于"sizeof(buffer)",感谢您指出这一点,但我100%确信这不是问题所在,因为在我的应用程序中,我不使用sizeof(buffer),而是使用ONE_MEGA(1024*1024)。

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

[更新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是问题所在?

如果所有东西都相等,那么内存就是内存,缓冲区是在堆上还是在堆栈上都无关紧要。

但很明显,并非所有事物都是平等的。我怀疑堆栈上1M缓冲区的分配与分配给OTHER线程的堆栈空间发生了INTERFERES/OVERLAPS。也就是说,增加堆栈需要重新定位当前线程的堆栈,或者重新定位其他线程的堆栈。这需要时间。当从堆中进行分配时,或者如果堆栈分配足够小而不会干扰,则不需要此时间,就像1K示例一样。

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

pthread_create
pthread_attr_getstack
pthread_attr_setstack

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

-Jeff

您忽略了recv的返回值。这不好。部分读取是生活中的一个事实,如果你通过这么大的缓冲区,很可能会这样。当您开始处理缓冲区中不包含有效数据的部分时,可能会发生意想不到的事情。

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

当然,对于动态分配的缓冲区,这一切都不应该有任何不同,但如果使用了sizeof (buffer),并且最终堆用户代码一次只读取指针大小的块,那么情况可能会有所不同。