2016-07-07 3 views
2

일정한 간격으로 이벤트를 발생시키는 대기 루프의 성능을 평가 중입니다. 나는 다음과 같은 코드를 사용하여 몇 가지 이상한 행동을 발견했습니다 :바쁜 대기 루프의 가변 성능?

테스트 시스템 (듀얼 14 코어 E5-2683 V3의 @의 2.00Ghz, 256기가바이트 DDR4), 루프의 20 만 반복에
#include <stdio.h> 
#include <stdlib.h> 
#include <unistd.h> 
#include <time.h> 

int timespec_subtract(struct timespec *, struct timespec, struct timespec); 

int main(int argc, char *argv[]) { 
    int iterations = atoi(argv[1])+1; 

    struct timespec t[2], diff; 

    for (int i = 0; i < iterations; i++) { 
     clock_gettime(CLOCK_MONOTONIC, &t[0]); 

     static volatile int i; 
     for (i = 0; i < 200000; i++) 
      ; 

     clock_gettime(CLOCK_MONOTONIC, &t[1]); 

     timespec_subtract(&diff, t[1], t[0]); 
     printf("%ld\n", diff.tv_sec * 1000000000 + diff.tv_nsec); 
    } 
} 

약이 1ms이다. 아니면하지 : 시간은 세 번째를 아래로 이동하면

1030854 
1060237 
1012797 
1011479 
1025307 
1017299 
1011001 
1038725 
1017361 
... (about 700 lines later) 
638466 
638546 
638446 
640422 
638468 
638457 
638468 
638398 
638493 
640242 
... (about 200 lines later) 
606460 
607013 
606449 
608813 
606542 
606484 
606990 
606436 
606491 
606466 
... (about 3000 lines later) 
404367 
404307 
404309 
404306 
404270 
404370 
404280 
404395 
404342 
406005 

, 그들은 때때로 수백 반복 약 450us까지 점프를 제외하고 (약 2 또는 3 마이크로 초 이내) 대부분 일관성을 유지. 이 동작은 유사한 시스템과 많은 실행에서 반복 가능합니다.

저는 바쁜 루프가 컴파일러에 의해 최적화 될 수 있다는 것을 알고 있습니다 만, 저는 그것이 그 문제라고 생각하지 않습니다. 무효화가 일어나지 않아야하고 갑자기 최적화를 설명하지 않기 때문에 캐시가 영향을 미쳐야한다고 생각하지 않습니다. 나는 또한 눈에 띄는 효과가없는 루프 카운터에 대해 레지스터 int를 사용하여 시도했다.

무슨 일이 일어나고 있는지, 그리고 어떻게하면 더 일관성있게 만드나요?

편집 : usleep, nanosleep 또는 10k 반복에 대한 대기 중 대기로이 프로그램을 실행하는 경우 모두 time -v으로 ~ 20000 개의 비 독자적 컨텍스트 스위치가 표시됩니다.

+0

점프 아마 컨텍스트 스위치입니다 전화 usleep/절전 지연에서 공제 할 수있는 시간 간격을 계산하는 gettimeofday를 사용할 수 있습니다. – Mysticial

+2

죄송합니다. 귀하의 접근 방식은 완전히 잘못되었습니다. 그런 식으로 모든 PC 시스템에서 신뢰할 수있는 타이밍을 얻을 수는 없습니다. 확실히 XY 문제입니다. ** 실제로 ** 수행하고 싶은 내용과 모든 관련 세부 사항을 기재하십시오. – Olaf

+0

내가 실제로하고 싶은 것은 질문 제목에 언급 한 바쁜 루프의 성능이 왜 바뀌는지를 이해하는 것입니다. 내 프로그램 타이밍을 정하는 대체 방법을 알고 있습니다. – Rakurai

답변

1

CPU 대기 리소스를 사용하는 것 외에도 대기 시간은 CPU 블록 속도에 크게 의존하게됩니다. 따라서 동일한 루프가 다른 컴퓨터에서 매우 다른 시간 동안 실행될 수 있습니다.

잠자기 방법에 문제가 있으면 OS 예약으로 인해 의도 한 것보다 오래자는 경우가 있습니다. nanosleep의 man 페이지는 신호를받은 경우 남은 시간을 알려주기 위해 rem 인수를 사용 하겠지만 너무 오래 기다리는 것에 대해서는 아무 것도 말하지 않습니다.

usleep을 호출 할 때마다 타임 스탬프를 가져와 실제로 잤다는 것을 알 수 있습니다. 너무 짧게 자면 적자를 더합니다. 너무 오래자는 경우 초과분을 뺍니다. 여기

내가 UFTP, 멀티 캐스트 파일 전송 응용 프로그램에서 이런 짓을하는 방법의 예, 일관된 속도로 패킷을 전송하기 위해 :

int64_t diff_usec(struct timeval t2, struct timeval t1) 
{ 
    return (t2.tv_usec - t1.tv_usec) + 
      (int64_t)1000000 * (t2.tv_sec - t1.tv_sec); 
} 

... 

     int32_t packet_wait = 10000; 
     int64_t overage = 0, tdiff; 
     struct timeval current_sent, last_sent; 

     gettimeofday(&last_sent, NULL); 

     while(...) { 
      ... 

      if (packet_wait > overage) { 
       usleep(packet_wait - (int32_t)overage); 
      } 
      gettimeofday(&current_sent, NULL); 
      tdiff = diff_usec(current_sent, last_sent); 
      overage += tdiff - packet_wait; 

      last_sent = current_sent; 
      ... 
     } 
+0

귀하의 의견을 보내 주셔서 감사합니다, 그것은 유용한 정보와 수면이 너무 길었을 때 타이밍을 제어하는 ​​올바른 방법입니다. 그러나, 그것은 내가 묻고있는 것이 아닙니다. 프로그램이 진행됨에 따라 바쁜 루프의 성능이 단계적으로 변경되는 이유를 파악하려고합니다. – Rakurai

+0

@Rakurai 이유는 전적으로 OS에 따라 다릅니다. 통화 대기 루프는이 때문에 매우 예측할 수 없습니다. OS 스케줄러의 소스 코드를 깊이 파고 들지 않는 한 (Linux 나 다른 오픈 소스 OS를 사용한다고 가정 할 때), 이것은 내려 가고자하는 경로가 아닙니다. – dbush

1

내가 만들 것 2 점 - 때문에 상황에 맞는 swtiching에 sleep/usleep은 예상보다 더 많은 시간 동안 잠들 수 있습니다. - 또한 인터럽트와 같은 우선 순위가 높은 작업이 있으면 잠자기가 전혀 실행되지 않을 수도 있습니다. 당신이 당신의 응용 프로그램에서 정확한 지연을 원하는 경우

따라서 당신은