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() の 3 つのステップはすべて CPU を集中的に使用する操作です。このような操作で CPU 使用率をできるだけ下げる方法はありますか (この方法を除く: "char buffer[1024]" などの非常に小さなバッファー サイズを設定します)。

ここでの問題は、アプリケーションが同じマシン上で別のサーバー アプリケーションと一緒に実行されるため、FileRelayer アプリケーションが CPU を消費しすぎないことです。そうしないと、他のサーバー アプリケーションが正常に動作しません。

[更新]
アプリケーションに関するいくつかの情報は次のとおりです
。 A. この FileServer マルチスレッド サーバー アプリケーションには約 70 のスレッドがありますが、すべてのソケットからデータを受信するために使用されるのはそのうちの 1 つだけです。
B. リスニング ソケットを含むすべてのソケットがノンブロッキング モードである。
C. アプリケーションが 4 つのクライアント (4 つのソケット) から 200 メガの 4 つのファイルを受信して​​いるときに、高い CPU 使用率 (80% - 90%) が検出されました。

問題について:
受信フロー全体を 2 つの主要な部分に分け、それらを FlowA と FlowB と呼びます。FlowA はソケットからデータを受信するだけです。FlowB は receive_data() でデータを処理する部分 (パケットのスライスなど) を表します。FlowA と FlowB はそれぞれ CPU 使用率が高くなることがわかりました。

1) FlowA: スタックから割り当てられた大きな配列 (1 メガ)、この投稿で示されています。私たちのテストでは、FlowA (ソケットからデータを受信した後にデータを破棄する) のみを残し、CPU 使用率が長時間にわたって 80 ~ 90% の高さに留まっていることがわかりました。そして、「char Buffer[ONE_MEGA]」を「char *buffer = new char[ONE_MEGA]」に置き換えると、CPU 使用率は 14% に減少します。
2) FlowA + FlowB: FlowA の問題を解決した後、今回は変動はあるものの、フロー全体 (FlowA + FlowB) で CPU 使用率が依然として 80% と高いことがわかりました。

受信バッファを char buffer[1024] などの非常に小さいものに設定すると、関数呼び出しごとに 1 つまたは 2 つのパケットしか処理しないため、CPU 使用率は劇的に減少しますが、転送速度も低下することが懸念されました。この問題を解決する他の方法はありますか?</p>

4

3 に答える 3

0

TCP ソケットの場合、機能receive_dataが正しく動作しない場合があります。

新しいローカル バッファーを割り当てるという事実は、関数が戻るときにこのバッファーが破棄されることを示唆しています。これは、receive_dataが不完全なメッセージを処理できないことを意味します。

正しいアプローチは、ソケットごとに一度バッファを割り当てることです。ソケットからそのバッファーに読み取り、バッファーの前にある完全なメッセージを処理して破棄します。すべての完全なメッセージが消費されたら、不完全なメッセージを含むバッファの末尾を先頭に移動し、次にソケットが読み取りの準備ができたときに、不完全なメッセージが完了するまで、不完全なメッセージの最後に新しいバイトを追加します。

于 2013-07-29T13:54:59.213 に答える
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 キャッシュに収まるように他のデータ項目と「戦う」ため、速度が遅くなるためです。

于 2013-07-29T14:52:55.747 に答える
0

はい。CPU は多くの作業を行ってはいけません。あなたが実際に行っている唯一のことは、バイトをコピーすることであり、それは不必要です。

于 2013-07-29T14:35:56.800 に答える