2009-10-28 4 views
2

:C++ 최적화 임시 변수 (P)의 사용 여부를 나는 다음과 같은 기능을 궁금하네요

void parse_foo(const char*& p_in_out, 
       foo& out) { 
    const char* p = p_in_out; 

    /* Parse, p gets incremented etc. */ 

    p_in_out = p; 
} 

또는 그냥 원래 인수를 사용하고 그것을 기대할 수 있습니다 어쨌든 위와 비슷하게 최적화 할 수 있습니까? 그러한 최적화가 있어야 할 것처럼 보이지만 위의 내용은 모질라 코드와 같은 일부 장소에서 "앨리어싱 방지"에 대한 모호한 의견이있는 것을 보았습니다.

답변

1

포인터를 변경하면 인수로 다시 반영되고 컴파일러는 더 빠른 코드를 생성 할 가능성이 더 높다는 것을 의미하지 않기 때문에 임시 변수가있는 변형이 더 빠를 수 있습니다. 그러나 이것을 테스트하는 올바른 방법은 해체를 컴파일하고 살펴 보는 것입니다.

한편으로 이것은 별칭 제거를 피하는 것과 관련이 있습니다. 실제로 이고 임시 변종은 앨리어싱을 사용합니다. 이제 동일한 배열에 두 개의 포인터가 생겨서 별칭이 무엇인지 정확히 알 수 있습니다.

+0

내가 빠른 코드에 대한 귀하의 이유를 이해하지 않습니다. –

+1

글쎄, 그것은 컴파일러의 어리 석음에 달려있다. 임시로 레지스터에 할당하고 인수에 한 번만 쓸 수 있습니다. 임시가 없으면 각 변경 사항에서 인수를 업데이트하기로 결정할 수 있습니다. – sharptooth

+0

그러나 함수를 실행하는 동안 레지스터에 매개 변수를 쉽게 넣을 수 있습니다. –

1

기능이 트랜잭션 일 가능성이있는 경우 임시로 사용합니다.

즉 함수가 완전히 성공하거나 실패합니다 (중간 지점 없음).
이 경우 함수가 실행되는 동안 상태를 유지하기 위해 temp를 사용하고 함수가 성공적으로 완료 될 때만 in_out 매개 변수에 다시 할당합니다. 직접 외부 상태를 반영하기 위해 수정 매개 변수를 사용하여 임시 (인 변화가 외부 포인터)

    • :

      기능이 조기에 종료 할 경우

      (예를 통해 예외) 우리는 두 가지 상황이 위치.

    두 가지 방법 모두 최적화 이점이 없습니다.

  • +0

    좋은 대답,이 그리고 sellibitze '대답은 돈에 맞다. 그것은 "완전히 실패"가 입력을 포기한다는 것을 의미합니다 (예외를 던지기 만하면 충분합니다). 그러나 입력에서 몇 가지 대안 중 하나를 인식해야 할 때, 제대로 트랜잭션 기능이 반드시 필요합니다. 감사! (죄송합니다 나는 아직 이것을 upvote 수 없습니다.) – mjmt

    0

    즉시 생각해 볼 수있는 한 가지 생각 : 예외 안전. 구문 분석 도중 예외가 발생하면 임시 변수는 강력한 예외 안전성을 제공하기 위해 수행해야하는 작업입니다. 함수 호출이 완전히 완료되었거나 아무 작업도 수행하지 않았습니다 (사용자 입장에서).

    +0

    그것이 필요한 경우. 예를 들어 함수가 스트림을 파싱하면 예외가 발생한 지점에서 포인터가 스트림의 지점을 나타낼 것으로 예상 할 수 있습니다. –

    +0

    참. 내가 가져다려고 노력한 것은 강력한 보증이 당신이 원하는 것이면 임시 포인터 변수가 갈 길이라는 것입니다. 이 "오류 위치"는 예를 들어 예외 객체의 일부일 수 있습니다. – sellibitze

    1

    예. restrict (MSVC는 __restrict)으로 표시되는 로컬에 할당해야합니다.

    그 이유는 경우 컴파일러는 p_in_out의 범위 포인트 아무것도, 그것은 로컬 레지스터에 포인터 아래의 내용을 저장할 수 없습니다 것을 절대적으로 확신 할 수 없다는 것입니다. 같은 범위의 다른 char *에 쓸 때마다 의 데이터를 읽어야합니다.. 이것은 "똑똑한"컴파일러인지 아닌지의 문제는 아닙니다. 정확성 요구 사항의 결과입니다.

    char* __restrict p을 작성하면 동일한 범위에있는 다른 포인터가 p과 같은 주소를 가리키는 컴파일러를 약속합니다. 이 보증이 없으면 *p 값은 다른 포인터가 기록 될 때마다 변경되거나 *p이 기록 될 때마다 다른 포인터의 내용을 변경할 수 있습니다.따라서 컴파일러는 *p에 대한 모든 할당을 즉시 메모리에 써야하고 다른 포인터를 쓸 때마다 다시 읽어야합니다.

    그래서,이 정확히 한 번 *p를로드 할 수 —을 발생하고 다른 포인터가 영향을 미치지 않습니다지지 않습니다 컴파일러를 보장하는 것은 — 성능이 향상 될 수 있습니다. 정확한 컴파일러와 상황에 따라 정확히 얼마나 달라질 수 있습니다 :로드 - 히트 - 스토어 패널티가 적용되는 프로세서에서는 엄청납니다. 대부분의 x86 CPU에서는 겸손합니다.

    여기에서 참조 포인터를 선호하는 이유는 단순히 포인터가 restrict이라고 표시 될 수 있으며 참조는 할 수 없다는 것입니다. C++이 그렇습니다.

    두 가지 방법을 시도해보고 결과를 측정하여 어느 것이 더 빠릅니다. 그리고 호기심이 있다면 I've written in depth on restrict and the load-hit-store elsewhere.

    부록는 : - 다른 뭔가가 아닌, const char *p가 저장되어있는 동일한 주소를 가리 수 있다는, 즉 위를 기록 후 나는 모즈의 사람들 자체가 별명되는 참조에 대한 자세한 걱정했다 실현 p가 가리키는 char. 하지만 내 대답은 동일합니다. 두껍게, const char *&pconst char **p을 의미하며 다른 포인터와 동일한 별칭 문제가 있습니다.

    +0

    C++에는 태그가 가리키는 "제한 사항"이 없습니다.그것은 C99 것입니다, 그리고 나는 C++ 0x로 들어가고 있다고 생각하지 않습니다. –

    +0

    난센스. GCC와 MSVC는 현재 구현에서이를 지원합니다. – Crashworks

    2

    좋은 답변이지만 성능 최적화가 걱정된다면 실제 파싱에 거의 모든 시간이 걸릴 것이므로 포인터 앨리어싱은 "잡음 있음"일 수 있습니다.

    +0

    파싱이 포인터에서 읽고 쓰는 것을 포함하지 않는 한. 그러면로드 - 히트 - 스토어가 내부 루프에있게됩니다. – Crashworks

    +0

    @ 크래시 : 다시 안녕하세요. 네가 옳아. 그런 식의 코드를 튜닝하는 경우, 여전히 충분히 빠르지 않다고 생각하고 내부 루프가 "병목 현상"이라고 생각하면 신경 쓰겠습니다. –

    +0

    ... 파서를 작성할 때, 대부분 다음과 같은 시간을 보냅니다 : while (WHITE (* p)) p ++;', 또는 테이블에서 심볼을 찾는다. 입력이 * 방대한 경우가 아니면 일반적으로 성능 문제는 아닙니다. –

    1

    p_in_out에 별칭이 지정되지 않았다는 것을 컴파일러에서 어떻게 알 수 있습니까? 참조를 통해 다시 데이터를 쓰는 것을 최적화 할 수는 없습니다.

    struct foo { 
        setX(int); setY(int); 
        const char* current_pos; 
    } x; 
    parse_foo(x.current_pos, x); 
    

    나는이보고 그냥 그런 다음 포인터에 대한 참조를 가지고 있지 않은 경우에 대한 원본을 수정 걱정할 필요가 없습니다 포인터를 반환하지 않은 이유를 부탁드립니다.

    const char* parse_foo(const char* p, foo& out) { 
        //use p; 
        return p; 
    } 
    

    그것은 또한 당신이를 rvalue로 함수를 호출 할 수 있습니다 의미 :

    p = parse_foo(p+2, out); 
    
    관련 문제