2013-07-29 3 views
0

우리는 clientA에서 clientB, clientC, clientD 등의 파일을 중계하는 서버 응용 프로그램을 가지고 있습니다. 우리는 이러한 종류의 파일 중첩을 작업으로 부릅니다. 실행중인 작업이 많으면 CPU 사용량이 매우 높습니다.FileServer에서 높은 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 집약적 인 작업입니다. 거기에 이런 종류의 작업 (가능하면 "char buffer [1024]"와 같은 매우 작은 버퍼 크기 설정)에서 CPU 사용량을 가능한 줄일 수있는 방법이 있습니까?

여기에서 우리의 응용 프로그램은 동일한 시스템에서 다른 서버 응용 프로그램과 함께 실행되므로 FileRelayer 응용 프로그램은 너무 많은 CPU를 소비 할 수 없으며 다른 서버 응용 프로그램은 정상적으로 작동하지 않습니다.

[UPDATE] 여기서
어플리케이션에 대한 정보의 일부 조각이다 :
A.가 모든 데이터를 수신하기 위해 사용되는 약 70이 파일 서버 스레드 서버 애플리케이션의 스레드 만, 이들의 하나는 소켓.
B. 모든 소켓은 수신 소켓을 포함하여 비 차단 모드입니다.
C. 응용 프로그램이 4 개의 클라이언트 (4 소켓)에서 200 메가의 파일 4 개를 수신하는 동안 높은 CPU 사용률 (80 % - 90 %)이 발견되었습니다. 이 문제에 관해서는

:
우리는 두 개의 주요 부분으로 전체 수신의 흐름을 분리, FlowA 및 FlowB를 호출 할 수 있습니다. FlowA는 소켓에서 데이터 만 수신합니다. FlowB는 패킷 분할과 같이 receive_data()에서 데이터를 처리하는 부분을 의미합니다. FlowA가 발견되어 FlowB가 각각 cpu 사용량을 높일 수 있습니다.

1) FlowA : 스택에서 할당 한 큰 배열 (1 메가), this post에 의해 분산 됨. 우리의 테스트에서는 FlowA (소켓에서 데이터를받은 후 데이터를 버립니다) 만 남겨두고 오랜 시간 동안 CPU 사용량이 80-90 %로 높습니다. 그리고 "char 버퍼 [ONE_MEGA]"를 "char * buffer = new char [ONE_MEGA]"로 대체하면 CPU 사용률이 14 %로 감소합니다.
2) FlowA + FlowB : FlowA의 문제를 해결 한 후에 전체 Flow (FlowA + FlowB)에서 CPU 사용량이 여전히 80 %로 높은 것으로 나타났습니다.

수신 버퍼를 char 버퍼 [1024]와 같이 매우 작게 설정하면 각 함수 호출에서 하나 또는 두 개의 패킷 만 처리하기 때문에 CPU 사용량이 크게 줄어들지 만 전송 속도 또한 저하 될 것이라고 걱정합니다 . 그래서이 문제를 해결할 다른 방법이 있습니까?

+0

높은 부하에서 높은 CPU 사용률이 실제로 좋습니다. 처리 알고리즘이 매우 비효율적이지 않으면 높은 사용량은 대기 시간이 거의 없음을 의미합니다. – dasblinkenlight

+0

@dasblinkenlight CPU 사용의 핵심을 지적 해 주셔서 감사합니다. ariticle을 업데이트했습니다. – Steve

+0

코드의 어느 부분이 더 큰 버퍼로 실제로 시간을 소비하는지 알아보십시오. 예를 들어 Linux에서'oprofile' 또는'perftool'을 사용하여 코드의 병목 현상을 확인할 수 있습니다. –

답변

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 캐시에 맞게 다른 데이터 항목 더 "싸움"것입니다, 그래서 느리다.