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

时间:2013-07-29 13:12:20

标签: c++ linux sockets networking cpu-usage

我们有一个服务器应用程序,它将文件从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 :(得分:0)

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

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

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

答案 1 :(得分:0)

是。 CPU不应该做太多工作。你唯一要做的就是复制字节and that is unnecessary

答案 2 :(得分: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缓存,因此速度较慢。