首页 文章

FileServer可以避免高CPU使用率吗?

提问于
浏览
0

我们有一个服务器应用程序,它将文件从clientA中继到clientB,clientC,clientD等 . 我们将这种文件中继称为任务 . 如果有许多任务正在执行,那么CPU使用率将非常高 .

我不知道在同时执行多个任务时这种高CPU使用率现象是否正常 . 有没有什么方法可以降低这种应用程序的CPU使用率?

//pseudo code
     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
                   **all fd are in non-blocking mode**
                   receive_data(fd);
               }
          }
     } 

     void receive_data(int fd){
          const int ONE_MEGA = 1024 * 1024;
          char *buffer = new char[ONE_MEGA]; 
          int readn = recv(fd, buffer, ONE_MEGA, 0);

          //handle the data: many steps
          char* DataToProcess = buffer;
          int LenToProcess = readn;
          while(LenToProcess > 0){
              1. scan the data to find the packet header
              2. get the length from the packet then perform checksum 
                 function which will scan every character of the packet 
                 to get a checksum value.
              3. if the packet is valid then add the packet to data queue. 
                 Move the buffer pointer and process the remaining data.
              ......
              LenToProcess -= DataProcessed;
              DataToProcess += DataProcessed; 
          };
     }

如您所见,receive_data()中的所有三个步骤都是cpu密集型操作 . 是否有任何方法可以在这种操作中尽可能地降低CPU使用率(除此之外:设置一个非常小的缓冲区大小,如“char buffer [1024]”)?

这里的问题是我们的应用程序将在同一台机器上运行另一个服务器应用程序,因此FileRelayer应用程序不能消耗太多的CPU,否则其他服务器应用程序将无法正常工作 - !

[UPDATE]
以下是有关该应用程序的一些信息:
答:此FileServer多线程服务器应用程序中大约有70个线程,但其中只有一个用于从所有套接字接收数据 .
B.所有套接字都处于非阻塞模式,包括监听套接字 .
C.当应用程序从4个客户端(4个插槽)接收4个200兆的文件时,发现高CPU使用率(80% - 90%) .

关于这个问题:
我们将整个接收流分为两个主要部分,我们称之为FlowA和FlowB . FlowA仅接收来自套接字的数据 . FlowB代表在receive_data()中处理数据的部分,如数据包切片等 . 我们发现FlowA和FlowB将分别导致高CPU使用率 .

1)FlowA:从堆栈分配的大数组(1兆),由this post拨打 . 在我们的测试中,我们只留下FlowA(从套接字接收后丢弃数据)并发现CPU使用率长时间保持高达80-90% . 并用"char *buffer = new char[ONE_MEGA]"替换"char Buffer[ONE_MEGA]",CPU使用率降低到14% .
2)FlowA FlowB:在我们解决了FlowA中的问题后,我们发现整个流程(FlowA FlowB)的CPU使用率仍然高达80%,尽管这次会有波动 .

将接收缓冲区设置为非常小的接收缓冲区(如char缓冲区[1024])会大大降低CPU使用率,因为每个函数调用它只会处理一个或两个数据包,但我们担心传输速度也会降低 . 那么还有其他方法可以解决这个问题吗?

3 回答

  • 0

    是 . CPU不应该正在复制字节,and that is unnecessary .

  • 0

    对于TCP套接字函数 receive_data 可能无法正常工作 .

    它分配一个新的本地缓冲区这一事实表明,当函数返回时,这个缓冲区会被破坏 . 这意味着 receive_data 无法处理不完整的消息 .

    正确的方法是为每个套接字分配一次缓冲区 . 从套接字读入该缓冲区,然后处理并丢弃缓冲区前面的完整消息 . 消耗完所有完整消息后,将包含不完整消息的缓冲区尾部移到前面,下次套接字准备好读取时,将新字节附加到不完整消息的末尾,直到完成为止 .

  • 0

    为了说明缓存的例子,我把我对类似主题的上一个问题的答案,并添加了一个校验和的代码片段:

    #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;
    const int S = 8*1024;
    
    void bigstack()
    {
        FILE *f = fopen("test.txt", "r");
        unsigned long long time;
        time = rdtsc();
        char buffer[M];
    
        fread(buffer, M, 1, f);
        int csum = 0;
        for(char i : buffer)
        {
        csum += i;
        }
        time = rdtsc() - time;
        fclose(f);
        cout << "bs: Time = " << time / 1000 << " csum=" << csum << endl;
    }
    
    
    void bigheap()
    {
        FILE *f = fopen("test.txt", "r");
        unsigned long long time;
        time = rdtsc();
        char *buffer = new char[M];
    
        fread(buffer, M, 1, f);
        int csum = 0;
        for(int i = 0; i < M; i++)
        {
        csum += buffer[i];
        }
        delete [] buffer;
        time = rdtsc() - time;
        fclose(f);
        cout << "bh: Time = " << time / 1000 << " csum=" << csum << endl;
    }
    
    
    void smallstack()
    {
        FILE *f = fopen("test.txt", "r");
        unsigned long long time;
        time = rdtsc();
        char buffer[S];
        int toread = M;
    
        int csum = 0;
        while(toread > 0)
        {
        fread(buffer, S, 1, f);
        for(char i : buffer)
        {
            csum += i;
        }
        toread -= S;
        }
        time = rdtsc() - time;
        fclose(f);
        cout << "ss: Time = " << time / 1000 << " csum=" << csum << endl;
    }
    
    
    int main()
    {
        for(int i = 0; i < 10; i++)
        {
        bigstack();
        bigheap();
        smallstack();
        }
    }
    

    所以,现在代码正在将数据读入CPU,然后遍历所有内容 . 进行大块所需的时间比较小的块高出约16% . 从下面可以看出,大块的时间大约是1400个单位的时间,而较小的块大小,即使多次调用 fread ,大约是1200个单位的时间 .

    这是输出的简化版本:

    bs: Time = 1417 csum=89411462
    bh: Time = 1428 csum=89411462
    ss: Time = 1208 csum=89411462
    bs: Time = 1444 csum=89411462
    bh: Time = 1415 csum=89411462
    ss: Time = 1205 csum=89411462
    bs: Time = 1463 csum=89411462
    bh: Time = 1409 csum=89411462
    ss: Time = 1262 csum=89411462
    bs: Time = 1418 csum=89411462
    bh: Time = 1441 csum=89411462
    ss: Time = 1213 csum=89411462
    

    原因是大块将与其他数据项“争夺”更多以适应CPU缓存,因此速度较慢 .

相关问题