簡體   English   中英

FileServer中可以避免CPU使用率過高嗎?

[英]Can High CPU usage be avoided in FileServer?

我們有一個服務器應用程序,它將文件從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,否則另一個服務器應用程序將無法正常工作!

[更新]
以下是有關該應用程序的一些信息:
答:此FileServer多線程服務器應用程序中大約有70個線程,但是其中只有一個用於從所有套接字接收數據。
B.所有套接字都處於非阻塞模式,包括偵聽套接字。
C.當應用程序從4個客戶端(4個套接字)接收四個200 Mega的文件時,發現CPU使用率很高(80%-90%)。

關於這個問題:
我們將整個接收流分為兩個主要部分,我們將它們稱為FlowA和FlowB。 FlowA僅從套接字接收數據。 FlowB代表在receive_data()中處理數據的部分,例如數據包切片等。我們發現FlowA和FlowB將分別導致較高的CPU使用率。

1)FlowA:從堆棧分配的大數組(1 Mega),由該帖子刪除。 在我們的測試中,我們僅保留FlowA(從套接字接收數據后丟棄數據),並且發現CPU使用率長​​時間保持高達80-90%。 將“ char Buffer [ONE_MEGA]”替換為“ char * buffer = new char [ONE_MEGA]”后,CPU使用率降低到14%。
2)FlowA + FlowB:解決了FlowA中的問題后,發現CPU使用率在整個流程(FlowA + FlowB)中仍然高達80%,盡管這次有所波動。

將接收緩沖區設置為非常小的緩沖區(例如char緩沖區[1024])將大大降低cpu的使用率,因為每個函數調用它只會處理一個或兩個數據包,但是我們擔心傳輸速度也會降低。 那么,還有其他方法可以解決這個問題嗎?

對於TCP套接字功能, receive_data可能無法正常工作。

它分配了一個新的本地緩沖區的事實表明該緩沖區在函數返回時被破壞。 這意味着receive_data無法處理不完整的消息。

正確的方法是為每個套接字分配一次緩沖區。 從套接字讀取該緩沖區,然后處理並丟棄緩沖區前面的完整消息。 一旦使用完所有完整的消息,請將包含不完整消息的緩沖區的尾部移到最前面,下一次套接字准備讀取時,將新字節追加到不完整消息的末尾,直到完成為止。

是。 CPU不應做太多工作。 您真正要做的唯一一件事就是在周圍復制字節, 而這是不必要的

為了說明使用緩存的示例,我回答了關於類似主題的上一個問題,並添加了一段校驗和代碼:

#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緩存,因此速度較慢。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM