2012-02-20 3 views
2

네트워킹 코드의 성능을 측정하려고하는데 매우 다양한 결과가 나타납니다. 지금까지 나는 그것을 설명 할 수 없었고, 다른 사람들은 올바른 방향으로 도울 수 있거나 지적 할 수있을 것입니다.C++에서 비 차단 소켓 쓰기 성능

socket(AF_INET, SOCK_STREAM, 0); 
int one = 1; 
setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one))); 
current = fcntl(socket, F_GETFL); 
fcntl(socket, F_SETFL, O_NONBLOCK | current); 

내 메시지가 항상 200 바이트 :

그래서 나는 소켓을 만들 수 있습니다. 메시지를 보냅니다 코드는 다음과 같습니다

uint64_t start (nanotimestamp()); 
unsigned char * buf; 
... 
//build a message 
//few calls to snprintf 
//buffer is preallocated 
... 
write(socket, buf, size); 
uint64_t end (nanotimestamp()); 

performance = end - start; 

uint64_t nanotimestamp() 
{ 
struct timespec now; 
clock_gettime(CLOCK_REALTIME, &now); 
return now.tv_sec * 1e9 + now.tv_nsec; 
} 

코드는 드문 경우> (100) 우리를 4.4 타이밍은 20에서 80 마이크로 초에 많은 변화 GCC로 컴파일 된 64 비트 레드햇 (6)에서 실행되고있다.

그래서 write에 대한 호출이 블로킹 (blocking)이 아닌 경우 왜 그런 차이가 나는가?

+0

네트워크 성능은 항상 네트워크 트래픽 조건의 영향을받습니다. 그것은 또한 그가 동일한 서브넷에 있거나 다른 – Swapnil

+0

인지 여부에 관계없이 다른 서버/클라이언트의 위치에 따라 달라집니다. 그러나 여기서는 네트워크 성능을 측정하지 않고 비 블로킹 소켓을 사용하고 있습니다. 아무것도 보내지 않으므로 성능은 전적으로 컴퓨터에서 실행해야합니다. – Tadzys

답변

0

쓰기를 호출하는 동안 프로세스가 일시 중지 될 수 있으므로이 프로세스는 1 회만 측정 할 수 없습니다 (이 값은 100us 이상임). 또한 syscall을 수행하면 약간의 차이가 발생할 수 있습니다.

더 자주 글을 쓰고 이러한 모든 호출이 결합 된 시간을 측정해야합니다.

0
  1. "비 차단"의무를 수행하는 데 얼마나 많은 시간이 걸리는지 알기 위해서만 write() 호출을 둘러싸는 것이 좋습니다.
  2. 해당 코드를 실행하는 스레드는 언제든지 커널에 의해 미리 준비 될 수 있습니다. 현재 코어에서 다른 스레드/프로세스를 예약합니다. 나중에 프로세스를 다시 예약합니다. 20-80us는 다른 프로세스의 실행 시간이 될 수 있습니다. (필자는 20-50 밀리 초를 더 기대했지만, 커널과 설정에 달려있다.)
+0

좋아, 아마 커널이 다른 프로세스 (내 프로세스는 단일 스레드)에 실행을 제공하는 것처럼 보입니다. renice로 실행 우선 순위를 높이면 대기 시간이 향상 될 수 있습니까? – Tadzys

+0

스케쥴러가 스레드의 퀀텀을 계산할 때 'nice'우선 순위를 사용하기 때문에 Renice는 확실히 도움이 될 것입니다. 그러나 스케줄러 클래스를 재설정하는 것이 더 좋습니다. "int sched_setscheduler (pid_t pid, int policy, const struct sched_param * p)"의 맨 페이지를 찾아 클래스를 SCHED_FIFO 또는 SCHED_RR로 변경하십시오. 귀하의 프로세스는 스케줄러에 의해 UN- premptable으로 고려 될 것입니다. 위대한주의와 함께 사용해야합니다! 프로세스가 잠시 (참) 루프 상태가되어 절대로 커널로 돌아 가지 않으면 단일 코어 장치가 '고정'됩니다. 또한 sched_setscheduler()를 호출하려면 루트 권한이 필요합니다. –

0

실제로 데이터를 전송하는 호출에서 EAGAIN으로 실패한 write() 호출을 분리하는 것은 흥미로운 일입니다. 불일치가 많이있을 수 있습니다.

또한이 시나리오에서 TCP_NODELAY가 실제로 도움이되는지 궁금합니다. 또한 그것을 토글 링할 가치가있을 수도 있습니다.

+0

분당 몇 개의 메시지를 보내는 경우 TCP_NODELAY를 제거하면 어떤 도움이 될까요? 메시지를 보내야 할 때 바로 나가야합니다. – Tadzys

2

이 측정에는 CLOCK_MONOTONIC을 사용하는 것이 좋습니다. 즉, CLOCK_REALTIME을 얻는 것보다 오버 헤드가 상당히 적습니다. 내 성능 측정을 위해 (우리가 나노초 정밀도를 필요로) 내가 RDTSC 카운터 사용

GCC와

4.4 (이 하나 확실하지 않은 100 %, 4.6.1 확실이 구현) 인텔 시스템을 다음과 같이 사용할 수 있습니다

다음 CPU 주파수 헤르츠 수로 CPU 주파수로 클럭 카운트의 델타를 나누어

extern "C" { 
    __inline__ uint64_t rdtsc() 
    { 
     uint32_t lo, hi; 
     __asm__ __volatile__ (
      "xorl %%eax,%%eax \n  cpuid" 
      ::: "%rax", "%rbx", "%rcx", "%rdx"); 
     __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); 
     return (uint64_t)hi << 32 | lo; 
    } 
} 

clock_gettime()

의 적은 비용으로 당신에게 매우 정확한 측정을 제공합니다 :

여부를 경우

#include <x86intrin.h> 
uint64_t clock_count = __rdtsc(); 

편집 :

그리고 이제 실제 질문 : 당신의 코드에서

에 대답하기 위해, 당신은 실제로 두 가지를 측정하고 - 메시지를 작성하고 전송.별도로 측정하거나 블록 외부에서 데이터를 이동할 수 있습니다. 마이크로 초를 측정 할 때 데이터 쓰기는 비용이 많이 듭니다.

나는이 문제가 snprintf()와 cache miss의 조합이라고 생각한다. 형식 함수는 성능이 매우 좋지 않으며 매번 데이터를 다시 작성하기 때문에 변동 가능성에 대한 질문에 대답해야하는 캐시 잠길 때마다 가 발생할 가능성이 있습니다.

1

다른 스레드, 하드웨어 또는 소프트웨어 인터럽트에 의한 중단이 이미 언급되었습니다.

다른 고려해야 할 것이 있습니다. 다양한 요인에 따라 논 블로킹 write() 호출은 매우 다른 코드 경로를 사용할 수 있습니다. 예 : 추가 버퍼를 할당해야 할 수도 있습니다. 시간이 걸리거나 버퍼가 필요하지 않을 수도 있습니다. 또는 데이터가 바로 전송되어야한다고 결정할 수 있으며, "곧바로 메탈로"갈 수 있습니다 (전송을 위해 네트워크 인터페이스에 데이터를 전달하기 위해 드라이버를 호출하십시오).

버퍼를 할당하는 데 시간이 걸리므로 데이터를 네트워크 인터페이스로 전달하는 데 더 많은 시간이 걸립니다.

write()는 기존 버퍼에서 데이터를 버퍼링하는 속도가 매우 빠르며, 추가 버퍼를 할당하는 속도가 조금 느려지거나 "실제"속도가 느릴 수도 있습니다.