IT이야기

if 문은 모듈로 전 및 할당 작업 전에 중복됩니까?

cyworld 2022. 6. 19. 18:27
반응형

if 문은 모듈로 전 및 할당 작업 전에 중복됩니까?

다음 코드 검토:

unsigned idx;
//.. some work with idx
if( idx >= idx_max )
    idx %= idx_max;

두 번째 줄만 간략화할 수 있습니다.

idx %= idx_max;

그리고 같은 결과를 얻을 것이다.


다음 코드를 여러 번 만났다.

unsigned x;
//... some work with x
if( x!=0 )
  x=0;

다음과 같이 단순화할 수 있다

x=0;

질문:

  • 사용할 수 있는 센스가 있나요?if그리고 왜?특히 ARM Thumb 명령어 세트에서는 더욱 그렇습니다.
  • 이것들은 할 수 있을까?if생략되어 있습니까?
  • 컴파일러는 어떤 최적화입니까?

컴파일러의 기능을 이해하려면 어셈블리를 꺼내기만 하면 됩니다.이 사이트(질문에서 이미 코드를 입력했습니다)를 추천합니다.https://godbolt.org/g/FwZZOb

첫 번째 예는 더 흥미롭다.

int div(unsigned int num, unsigned int num2) {
    if( num >= num2 ) return num % num2;
    return num;
}

int div2(unsigned int num, unsigned int num2) {
    return num % num2;
}

생성:

div(unsigned int, unsigned int):          # @div(unsigned int, unsigned int)
        mov     eax, edi
        cmp     eax, esi
        jb      .LBB0_2
        xor     edx, edx
        div     esi
        mov     eax, edx
.LBB0_2:
        ret

div2(unsigned int, unsigned int):         # @div2(unsigned int, unsigned int)
        xor     edx, edx
        mov     eax, edi
        div     esi
        mov     eax, edx
        ret

기본적으로 컴파일러는 매우 구체적이고 논리적인 이유로 브랜치를 최적화하지 않습니다.정수 나눗셈이 비교와 거의 같은 비용이라면 분기는 의미가 없습니다.그러나 정수 나눗셈(일반적으로 와 함께 수행되는 계수)은 실제로 매우 비용이 많이 듭니다.http://www.agner.org/optimize/instruction_tables.pdf이 수치는 아키텍처 및 정수 크기에 따라 크게 다르지만 일반적으로 대기 시간이 15 ~ 100 사이클에 이를 수 있습니다.

계수를 실행하기 전에 가지를 치면 실제로 많은 수고를 덜 수 있습니다.단, 컴파일러는 브런치가 없는 코드를 어셈블리레벨의 브런치로 변환하지 않습니다.그 이유는 지점에도 단점이 있기 때문입니다.계수가 어차피 필요하게 되면 시간을 조금 낭비하게 되는 것입니다.

적절한 최적화에 대해 적절한 판단을 내릴 수 있는 방법은 상대적인 빈도를 모르면 없습니다.idx < idx_max현실이 될 거야따라서 컴파일러(gcc와 clang)는 비교적 투과적인 방법으로 코드를 매핑하는 방법을 선택하므로 이 선택은 개발자의 손에 맡겨집니다.

그래서 그 지점이 아주 합리적인 선택이었을 수도 있어요.

비교와 할당은 비슷한 비용이 들기 때문에 두 번째 분기는 완전히 무의미해야 합니다.즉, 컴파일러가 변수를 참조하고 있는 경우에도 이 최적화는 실행되지 않는 것을 링크에서 확인할 수 있습니다.값이 로컬 변수인 경우(표시된 코드와 같이), 컴파일러는 브랜치를 최적화합니다.

요약하면 첫 번째 코드는 합리적인 최적화일 수 있고 두 번째 코드는 아마도 피곤한 프로그래머일 수 있습니다.

이미 보유하고 있는 값을 사용하여 변수를 쓰는 것이 읽기보다 느릴 수 있고, 원하는 값을 이미 보유하고 있음을 확인하고 쓰기를 건너뛸 수 있습니다.일부 시스템에는 모든 쓰기 요청을 즉시 메모리로 전송하는 프로세서 캐시가 있습니다.오늘날에는 이러한 설계가 일반적이지 않지만, 이전에는 전체 읽기/쓰기 캐싱이 제공하는 성능 향상 중 상당한 부분을 제공할 수 있지만 적은 비용으로 제공할 수 있기 때문에 매우 일반적이었습니다.

위와 같은 코드는, 멀티 CPU 의 상황에도 관련하는 경우가 있습니다.가장 일반적인 상황은 2개 이상의 CPU 코어로 동시에 실행 중인 코드가 변수에 반복적으로 히트하는 경우입니다.강력한 메모리 모델을 가진 멀티코어 캐싱 시스템에서는 변수를 쓰는 코어는 먼저 다른 코어와 네고시에이트하여 캐시 라인의 배타적 소유권을 취득하고, 다음에 다른 코어가 읽기 또는 쓰기를 원할 때 이러한 제어를 포기하도록 다시 네고시에이트해야 합니다.이러한 운영은 매우 비용이 많이 들기 쉬우며, 모든 쓰기가 단순히 스토리지가 보유하고 있는 가치를 저장하는 것일지라도 비용은 고스란히 부담해야 합니다.단, 로케이션이0이 되어 다시 기입되지 않으면 양쪽 코어가 비배타적인 읽기 전용 액세스를 위해 캐시 라인을 동시에 유지할 수 있으므로 더 이상 네고시에이트할 필요가 없습니다.

여러 CPU가 변수에 영향을 줄 수 있는 거의 모든 상황에서 변수는 최소한 선언해야 합니다.volatile여기서 적용할 수 있는 유일한 예외는 다음 명령어를 시작한 후에 발생하는 변수에 대한 모든 쓰기입니다.main()는 같은 값을 저장하며, 어떤 CPU의 저장소가 다른 CPU에 표시되는지 여부에 관계없이 코드는 올바르게 동작합니다.몇 가지 동작을 여러 번 실행하는 것이 낭비이지만 그 이외의 조작에는 해가 없는 경우, 변수의 목적은 그것이 필요한지 아닌지를 판단하는 경우, 많은 구현에서 더 나은 코드를 생성할 수 있습니다.volatile조건 없이 쓰기를 함으로써 효율성을 향상시키려 하지 않는 한 한정자보다 낫다.

덧붙여서, 오브젝트가 포인터를 통해 액세스 된 경우, 위의 코드에는 다른 생각할 수 있는 이유가 있을 것입니다: 함수가 다음 중 하나를 받아들이도록 설계되어 있는 경우const특정 필드가 0인 객체 또는 비필드의 객체const두 경우 모두 정의된 동작을 보증하기 위해 위와 같은 코드가 필요할 수 있습니다.

코드 첫 번째 블록에 대해서: 이것은 Clang에 대한 Chandler Karuth의 권장(자세한 내용은 여기를 참조)에 근거한 마이크로 최적화이지만, 반드시 이 형태(3진법이 아닌 사용)나 특정 컴파일러에 유효한 마이크로 최적화라고는 할 수 없습니다.

Modulo는 상당히 비용이 많이 드는 작업입니다.코드가 자주 실행되고 있고 조건의 한쪽 또는 다른 쪽에 통계적으로 강한 치우침이 있는 경우 CPU의 분기 예측(현대 CPU가 주어짐)에 의해 분기 명령 비용이 대폭 절감됩니다.

거기에 있는 것을 사용하는 것은 나에게 있어서 좋지 않은 생각인 것 같다.

맞아요.유무idx >= idx_max다음 시간 이후 idx_max 아래에 있습니다.idx %= idx_max.한다면idx < idx_max를 따르든 안 따르든 변경되지 않습니다.

모듈로를 중심으로 분기하면 시간을 절약할 수 있다고 생각할 수도 있지만, 브런치를 따라갈 때 최신 CPU의 파이프라인을 리셋해야 하기 때문에 상대적으로 많은 시간이 소요됩니다.정수 나눗셈과 거의 같은 시간이 소요되는 정수 모듈로를 수행하는 것보다 분기를 따르지 않는 것이 좋습니다.

EDIT: 다른 사람들이 제안하듯이 계수값이 브랜치에 비해 상당히 느리다는 것이 판명되었습니다.이 질문에 대해 검토 중인 사람이 있습니다: CppCon 2015: Chandler Karuth "Tuning C++: Benchmarks, CPUs, and Compiler! 마이!" (이 질문에 대한 다른 답변과 링크된 다른 SO 질문에서 제안됨)

이 사람은 컴파일러를 쓰고 있어, 브랜치가 없는 편이 고속이라고 생각하고 있었습니다만, 벤치마크에 의해서 틀린 것이 판명되었습니다.브런치를 20%만 사용해도 테스트 속도가 빨라졌습니다.

또 다른 이유는 다음과 같습니다.관리해야 할 코드 한 줄 줄 줄도 줄었고, 다른 누군가가 그 의미를 알아내야 했습니다.위의 링크에 있는 남자는 실제로 "fast modulus" 매크로를 만들었습니다.IMHO, 즉 인라인 함수는 퍼포먼스가 중요한 애플리케이션을 위한 방법입니다.브런치 없이도 코드를 쉽게 이해할 수 있지만 실행 속도는 그만큼 빠르기 때문입니다.

마지막으로 위의 비디오에 있는 남자는 이 최적화를 컴파일러 라이터에게 알릴 계획입니다.따라서 if는 코드에 포함되지 않더라도 추가될 수 있습니다.따라서 이 경우 mod만으로 충분합니다.

언급URL : https://stackoverflow.com/questions/43731203/is-the-if-statement-redundant-before-modulo-and-before-assign-operations

반응형