"IF"가 비싸나요?


98

저는 제 삶을 위해 우리 선생님이 그날 정확히 말한 것을 기억할 수 없으며 아마도 당신이 알기를 바랍니다.

모듈은 "데이터 구조 및 알고리즘"이며 그는 다음과 같은 내용을 우리에게 말했습니다.

if문은 가장 비싼 [뭔가]입니다. [무언가]는 [무언가]를 등록합니다.

네, 끔찍한 기억이 있고 정말 미안하지만 몇 시간 동안 인터넷 검색을했는데 아무것도 나오지 않았습니다. 어떤 아이디어?


29
선생님에게 옵션을 요청하고 있습니까?
Michael Myers

7
선생님에게 이메일을 보내지 않으시겠습니까? 당시에 거기에 없었거나 선생님이 그렇게 읽지 않는 한 선생님이 말한 것을 아는 사람은 없을 것입니다.
Bill Karwin

11
그리고 물론 필수 철도 답변에 대한
bobobobo

C 영향을받는 중괄호 언어의 If 문 또는 특히 "? :"식은 x86 및 arm 프로세서와 같은 특수 조건부 실행 명령으로 구현할 수 있습니다. 이전 테스트를 기반으로 일부 작업을 수행하거나 수행하지 않는 지침입니다. 이 훌륭한 명령어를 사용하면 조건부 점프 / 분기 / 'goto'명령어가 모두 필요하지 않습니다. 코드의 다른 지점으로 뛰어 다니지 않고 (예측 불가능한) 직선으로 쟁기질하기 때문에 프로그램 흐름을 완전히 예측 가능하게 만들어 일부 상황에서 엄청난 성능 향상.
Cecil Ward

좋은 컴파일러는 코드를 재구성하고 식 또는? : 표현. 당신이 정말로 당신의 asm을 알고 있고 예를 들어 Agner Fog의 최적화 가이드를 읽지 않았다면 이것을 가지고 놀지 마십시오. 컴파일러는 if 문이든? : 표현식이 사용됩니다.
Cecil Ward

답변:


185

(하드웨어에서) 매우 낮은 수준에서, 예, 경우 들 비싸다. 이유를 이해하려면 파이프 라인의 작동 방식을 이해해야합니다 .

실행될 현재 명령은 일반적으로 명령 포인터 (IP) 또는 프로그램 카운터 (PC) 라고하는 것에 저장됩니다 . 이러한 용어는 동의어이지만 아키텍처마다 다른 용어가 사용됩니다. 대부분의 명령어에서 다음 명령어의 PC는 현재 PC에 현재 명령어의 길이를 더한 것입니다. 대부분의 RISC 아키텍처에서 명령어는 모두 일정한 길이이므로 PC를 일정한 양만큼 증가시킬 수 있습니다. x86과 같은 CISC 아키텍처의 경우 명령어는 가변 길이가 될 수 있으므로 명령어를 디코딩하는 로직은 현재 명령어가 다음 명령어의 위치를 ​​찾는 데 걸리는 시간을 파악해야합니다.

들어 가지 지침, 그러나 실행되는 다음 명령은 현재 명령 후 다음 위치가 아닙니다. 분기는 gotos입니다-프로세서에게 다음 명령어가 어디에 있는지 알려줍니다. 분기는 조건부 또는 무조건 부일 수 있으며 대상 위치는 고정 또는 계산 될 수 있습니다.

조건부 대 무조건 부는 이해하기 쉽습니다. 조건부 분기는 특정 조건이 유지되는 경우에만 사용됩니다 (예 : 한 숫자가 다른 숫자와 같은지 여부). 분기를 취하지 않으면 정상적으로 분기 후 다음 명령으로 제어가 진행됩니다. 무조건 분기의 경우 항상 분기가 사용됩니다. 조건부 분기는 if문과 forwhile루프 의 제어 테스트에 표시됩니다 . 무조건 분기는 무한 루프, 함수 호출, 함수 반환 breakcontinue명령문, 악명 높은 goto명령문 등에서 표시됩니다 (이 목록은 완전하지 않습니다).

지점 대상은 또 다른 중요한 문제입니다. 대부분의 분기에는 고정 분기 대상이 있습니다. 컴파일 타임에 고정되는 코드의 특정 위치로 이동합니다. 여기에는 if문, 모든 종류의 루프, 일반 함수 호출 등 이 포함됩니다 . 계산 된 분기는 런타임에 분기의 대상을 계산합니다. 여기에는 switch명령문 (때때로), 함수에서 반환, 가상 함수 호출 및 함수 포인터 호출이 포함됩니다.

그렇다면이 모든 것이 성능에 어떤 의미가 있습니까? 프로세서가 파이프 라인에 분기 명령이 표시되는 것을 확인하면 파이프 라인을 계속 채울 방법을 파악해야합니다. 프로그램 스트림에서 분기 다음에 오는 명령어를 파악하려면 (1) 분기를 사용할 것인지 (2) 분기 대상을 알아야합니다. 이를 파악하는 것을 분기 예측 이라고 하며 이는 어려운 문제입니다. 프로세서가 올바르게 추측하면 프로그램이 최대 속도로 계속됩니다. 대신 프로세서가 잘못 추측 하면 잘못된 것을 계산하는 데 시간이 걸립니다. 이제 파이프 라인을 플러시하고 올바른 실행 경로의 명령으로 다시로드해야합니다. 결론 : 큰 성능 저하.

따라서 if 문이 비싼 이유는 분기 예측 오류 때문 입니다. 이것은 가장 낮은 수준입니다. 고급 코드를 작성하는 경우 이러한 세부 사항에 대해 전혀 걱정할 필요가 없습니다. 성능이 매우 중요한 코드를 C 또는 어셈블리로 작성하는 경우에만주의해야합니다. 이 경우 몇 가지 명령이 더 필요하더라도 분기없는 코드를 작성하는 것이 분기하는 코드보다 우월 할 수 있습니다. 당신은 같은 일을 계산하기 위해 할 수있는 멋진 비트 만지작 트릭이있다 abs(), min()max()분기없이.


20
그것은 아닙니다 단지 잘못된 분기 예측. 분기는 또한 컴파일러 수준에서 그리고 어느 정도 CPU 수준에서 명령어 재정렬을 금지합니다 (물론 순서가 잘못된 CPU의 경우). 그래도 좋은 자세한 대답.
jalf

5
고수준 언어가 궁극적으로 저수준 언어로 번역되고 매우 성능 중심 코드를 작성하는 경우 if 문을 피하는 코드를 작성해도 여전히 아무것도 얻지 못합니까? 이 개념은 더 높은 수준의 언어에 적용되지 않습니까?
c ..

18

"비싸다"는 매우 상대적인 용어이며 특히 "if "문과의 에서 조건의 비용도 고려해야하기 때문입니다. 이는 몇 가지 짧은 CPU 명령어에서 원격 데이터베이스를 호출하는 함수의 결과를 테스트하는 것까지 다양합니다.

나는 그것에 대해 걱정하지 않을 것입니다. 임베디드 프로그래밍을 수행하지 않는 한 " if" 의 비용에 대해 전혀 걱정할 필요가 없습니다 . 대부분의 프로그래머를 들어 그냥 않을거야 지금까지 앱의 성능을 구동 요인.


1
확실히 상대적인 ... cmp / cond jmp는 여전히 많은 프로세서에서 mul보다 빠릅니다.
Brian Knoblauch

4
예, 나는 그것에 대해 걱정하지 말아야한다는 데 동의합니다. 여기서는 최적화하려는 것이 아닙니다. 나는 단지 알아 내고 배우려고 노력하고 있습니다. ;)
pek

15

특히 RISC 아키텍처 마이크로 프로세서의 분기는 가장 값 비싼 명령어 중 일부입니다. 이는 많은 아키텍처에서 컴파일러가 가장 가능성이 높은 실행 경로를 예측하고 해당 명령을 실행 파일에 배치하므로 분기가 발생할 때 이미 CPU 캐시에 있기 때문입니다. 브랜치가 다른 방향으로 가면 메인 메모리로 돌아가서 새 명령어를 가져와야합니다. 이는 상당히 비쌉니다. 많은 RISC 아키텍처에서 모든 명령어는 분기 (종종 2주기)를 제외하고 하나의주기입니다. 여기서는 주요 비용에 대해 이야기하는 것이 아니므로 걱정하지 마십시오. 또한 컴파일러는 99 %의 시간보다 더 잘 최적화됩니다. ) EPIC 아키텍처 (Itanium이 예)에 대한 정말 멋진 점 중 하나는 브랜치의 양쪽에서 명령을 캐시 (및 처리 시작) 한 다음 브랜치의 결과가 나오면 필요하지 않은 세트를 폐기한다는 것입니다. 모두 다 아는. 이렇게하면 예상치 못한 경로를 따라 분기되는 경우 일반적인 아키텍처의 추가 메모리 액세스가 절약됩니다.


13

셀 성능에 대한 분기 제거통한 성능 향상 기사를 확인하십시오 . 또 다른 재미있는 것은 Real Time Collision Detection Blog의 분기없는 선택대한이 게시물 입니다.

이 질문에 대한 답변으로 이미 게시 된 우수한 답변 외에도 "if"문이 값 비싼 저수준 작업으로 간주되지만 상위 수준 환경에서 분기없는 프로그래밍 기술을 활용하려고한다는 점을 상기시켜 드리고 싶습니다. 스크립팅 언어 또는 비즈니스 로직 레이어 (언어에 관계없이)와 같은은 엄청나게 부적절 할 수 있습니다.

대부분의 경우 프로그램은 먼저 명확성을 위해 작성되고 성능을 위해 최적화되어야합니다. 성능이 가장 중요한 문제가 많은 영역이 있지만 대부분의 개발자는 렌더링 엔진의 핵심 또는 몇 주 동안 실행되는 고성능 유체 역학 시뮬레이션의 핵심에서 사용할 모듈을 작성하지 않습니다. 솔루션이 "그냥 작동"하는 것이 최우선 과제 인 경우 마지막으로 생각하는 것은 코드에서 조건 문의 오버 헤드를 줄일 수 있는지 여부입니다.


과연! 또한 호출을 장려하는 언어 (기본적으로 어셈블러 또는 stdlib가없는 C 제외)로 코딩 할 때 일반 프로그래밍 기술의 파이프 라인 간섭이 조건부 분기에 대한 질문을 압도 할 것이라고 덧붙일 수 있습니다.
Ross Patterson 19 년

10

if그 자체로는 느리지 않습니다 . 느림은 항상 상대적으로 당신이 if- 문의 "오버 헤드"를 느껴본 적이 없다고 확신합니다. 고성능 코드를 만들려면 어쨌든 분기를 피하고 싶을 것입니다. if느리게 만드는 것은 프로세서가 if휴리스틱과 그 밖의 것을 기반으로 한 후 코드를 미리로드한다는 것 입니다. 또한 if프로세서가 아직 어떤 경로를 사용할지 알지 못하기 때문에 파이프 라인 이 기계 코드 의 분기 명령어 바로 뒤에서 코드를 실행 하지 못하도록 차단합니다 (파이프 라인 프로세서에서는 여러 명령이 인터리브되고 실행 됨). 실행 된 코드는 역순으로 실행되거나 (다른 브랜치가 사용 된 경우라고 함 branch misprediction) noop이러한 위치에 채워져 이런 일이 발생하지 않습니다.

경우 if악하고 switch악한도, 그리고 &&, ||너무. 그것에 대해 걱정하지 마십시오.


7

가능한 가장 낮은 수준 if은 다음과 같이 구성됩니다 (특정에 대한 모든 앱별 전제 조건을 계산 한 후 if).

  • 일부 테스트 지침
  • 테스트가 성공하면 코드의 특정 위치로 이동하고 그렇지 않으면 앞으로 진행합니다.

그와 관련된 비용 :

  • 낮은 수준의 비교-일반적으로 1 CPU 작업, 매우 저렴
  • 잠재적 인 점프-비용이 많이들 수 있음

점프가 비싸다는 이유에 대한 공명 :

  • CPU에 의해 캐시되지 않은 것으로 밝혀지면 메모리에있는 임의의 코드로 건너 뛸 수 있습니다. 문제가 있습니다.
  • 최신 CPU는 분기 예측을 수행합니다. 성공 여부를 추측하고 파이프 라인에서 코드를 미리 실행하기 때문에 속도가 빨라집니다. 예측이 실패하면 파이프 라인에서 수행 한 모든 계산이 무효화되어야합니다. 그것은 또한 비싼 작업입니다

요약하자면 :

  • 만약 당신이 정말로, 정말로, 정말로 성능에 관심이 있다면.
  • 실시간 레이트 레이서 나 생물학적 시뮬레이션 또는 이와 유사한 것을 작성 하는 경우에만 관심을 가져야합니다 . 대부분의 현실 세계에서 그것에 대해 신경 쓸 이유가 없습니다.

다음 단계로 넘어가십시오. 중첩 및 / 또는 복합 if 문은 어떻습니까? 누군가가 이와 같은 if 문을 많이 작성하면 비용이 빠르게 눈에 띄게 될 수 있습니다. 그리고 대부분의 개발자에게 if 문이 기본적인 연산처럼 보이기 때문에 복잡한 조건부 분기를 피하는 것은 종종 문체 문제로 넘어갑니다. 스타일에 대한 우려는 여전히 중요하지만, 순간적으로 무시할 첫 번째 관심사가 될 수 있습니다.
jaydel

7

최신 프로세서에는 긴 실행 파이프 라인이 있으므로 여러 명령이 동시에 여러 단계에서 실행됩니다. 다음 명령이 실행되기 시작할 때 한 명령의 결과를 항상 알 수는 없습니다. 조건부 점프 (만약)가 발생하면 파이프 라인이 비워 질 때까지 기다려야 명령 포인터가 어느 방향으로 이동해야하는지 알 수 있습니다.

나는 그것을 긴화물 열차라고 생각한다. 많은화물을 직선으로 빠르게 운반 할 수 있지만 코너가 심하게 나옵니다.

Pentium 4 (Prescott)는 31 단계의 긴 파이프 라인을 가지고있었습니다.

Wikipedia 에 대한 추가 정보


3
화물 열차 은유에 +1-다음에 프로세서 파이프 라인을 설명 할 필요가 있음을 기억할 것입니다.
Daniel Pryden

6

분기가 CPU 명령 프리 페치를 죽일 수 있습니까?


나의 ... "연구"에서 나는 switch 문에 대한 점프 테이블과 분기에 대해 배웠지 만 if 문에 대해서는 아무것도 배웠다. 그것에 대해 조금 더 자세히 설명해 주시겠습니까?
pek

IIRC, CPU는 일반적으로 단일 가능한 실행 경로를 따라 명령어를 프리 페치하지만, 예측 된 실행 경로에서 분기를 발생시키는 'if'문은 프리 페치 된 명령어를 무효화하고 사전 기술을 다시 시작해야합니다.
activout.se

괜찮은 프로세서라면 브랜치를 취할지 여부를 추측 할 수있는 브랜치 예측 기능과 예측을 기반으로 한 프리 페치 명령 (일반적으로 매우 좋음)이 있어야합니다. GCC에는 프로그래머가 분기 예측 자에 대한 힌트를 제공 할 수있는 C 확장도 있습니다.
mipadi

2
더욱이 CPU는 일반적으로 다가오는 명령을 미리 가져 오는 것이 아니라 조기에 실행하기 시작하고 컴파일러는 명령을 재정렬하려고 시도하며 이는 분기간에 위험 해 지므로 분기가 너무 많은 명령 스케줄링을 실제로 중단 할 수 있습니다. 성능이 저하됩니다.
jalf

6

또한 루프 내부가 반드시 비용이 많이 드는 것은 아닙니다 .

최신 CPU는 if- 문을 처음 방문 할 때 "if-body"를 취해야한다고 가정합니다 (또는 반대로 말하면 루프 본문도 여러 번 취해지는 것으로 가정합니다) (*). 두 번째 및 추가 방문시, CPU (CPU)는 분기 내역 테이블을 볼 수 있습니다. 조건이 마지막으로 어땠는지 확인할 수 있습니다 (참 이었습니까? 거짓 이었습니까?). 지난번에 거짓 이었다면, 추론 적 실행은 if의 "else"또는 루프를 넘어서 진행됩니다.

(*) 규칙은 실제로 " 정방향 분기를 사용하지 않고 역방향 분기를 사용합니다 "입니다. if- 문 에서 조건이 false로 평가되면 ( if-body 다음 지점까지) [forward] 점프 만 있습니다 (기억하십시오 : CPU는 어쨌든 분기 / 점프를 취하지 않는다고 가정). , 루프 이후 위치에 대한 전방 분기 (취하지 않음)와 반복시 후방 분기 (취득)가있을 수 있습니다.

이것은 또한 가상 함수 또는 함수 포인터 호출에 대한 호출이 많은 사람들이 가정하는 것만 큼 나쁘지 않은 이유 중 하나입니다 ( http://phresnel.org/blog/ )


5

많은 사람들이 지적했듯이 조건부 분기는 현대 컴퓨터에서 매우 느릴 수 있습니다.

즉, if 문에 존재하지 않는 조건부 분기가 많이 있습니다. 컴파일러가 무엇을 만들지 항상 알 수는 없으며 기본 문에 걸리는 시간에 대해 걱정하는 것은 사실상 항상 잘못된 것입니다. 할 것. (컴파일러가 안정적으로 생성 할 내용을 알 수 있다면 최적화 컴파일러가 좋지 않을 수 있습니다.)


4

이것이 언급 할 수 있다고 상상할 수있는 유일한 것은 if 진술이 일반적으로 분기를 생성 할 수 . 프로세서 아키텍처의 특성에 따라 분기는 파이프 라인 중단 또는 기타 최적의 상황이 아닐 수 있습니다.

그러나 이것은 매우 상황에 따라 다릅니다. 대부분의 최신 프로세서에는 분기의 부정적인 영향을 최소화하려는 분기 예측 기능이 있습니다. 또 다른 예는 ARM 아키텍처 (및 기타)가 조건부 논리를 처리 할 수있는 방법입니다. ARM에는 명령어 수준 조건부 실행이 있으므로 간단한 조건부 논리로 인해 분기가 발생하지 않습니다. 조건이 충족되지 않으면 명령이 NOP로 실행됩니다.

말한 모든 것-이 물건에 대해 걱정하기 전에 논리를 수정하십시오. 잘못된 코드는 최대한 최적화되지 않았습니다.


ARM의 조건부 명령어가 ILP를 억제하여 문제를 해결할 수 있다고 들었습니다.
JD

3

CPU는 깊이 파이프 라인되어 있습니다. 분기 명령어 (if / for / while / switch / etc)는 CPU가 다음에로드하고 실행할 명령어를 실제로 알지 못한다는 것을 의미합니다.

CPU가 수행 할 작업을 기다리는 동안 중단되거나 CPU가 추측합니다. 오래된 CPU의 경우 또는 추측이 잘못된 경우 올바른 명령을로드하고로드하는 동안 파이프 라인 지연이 발생해야합니다. CPU에 따라 이것은 10 ~ 20 개의 명령 치가 될 수 있습니다.

최신 CPU는 좋은 분기 예측을 수행하고 동시에 여러 경로를 실행하고 실제 경로 만 유지하여이를 방지하려고합니다. 이것은 많은 도움이되지만 지금까지만 갈 수 있습니다.

수업에서 행운을 빕니다.

또한 실생활에서 이것에 대해 걱정해야한다면 아마도 OS 디자인, 실시간 그래픽, 과학 컴퓨팅 또는 이와 유사한 CPU 바인딩을 수행하고있을 것입니다. 걱정하기 전에 프로필.


2

분명히 비효율적이지 않은 가장 명확하고 간단하며 깨끗한 방법으로 프로그램을 작성하십시오. 이는 가장 값 비싼 리소스를 최대한 활용합니다. 프로그램을 작성하거나 나중에 디버깅 (이해 필요)합니다. 성능이 충분하지 않으면 측정병목 현상이있는 곳과이를 완화하는 방법을 확인하십시오. 매우 드문 경우에만 그렇게 할 때 개별 (소스) 지침에 대해 걱정해야합니다. 성능은 첫 번째 줄에서 올바른 알고리즘과 데이터 구조를 선택하고 신중한 프로그래밍, 충분히 빠른 시스템을 얻는 것입니다. 좋은 컴파일러를 사용하면 현대 컴파일러가 코드를 재구성하는 것을 보면 놀랄 것입니다. 성능을 위해 코드를 재구성하는 것은 일종의 최후의 수단이며, 코드는 더 복잡해지고 (따라서 버그가 더 커지고) 수정하기가 더 어려워지고 따라서 전반적으로 더 비쌉니다.



0

나는 한 번 내 친구와이 논쟁을했다. 그는 매우 순진한 원 알고리즘을 사용하고 있었지만 내 것보다 더 빠르다고 주장했습니다 (원의 1/8 만 계산하는 종류). 결국 if 문은 sqrt로 대체되었으며 어떻게 든 더 빨랐습니다. 아마도 FPU에 sqrt가 내장되어 있기 때문일까요?


-1

ALU 사용 측면에서 가장 비쌉니까? 비교할 값을 저장하기 위해 CPU 레지스터를 사용하고 if 문이 실행될 때마다 값을 가져오고 비교하는 데 시간이 걸립니다.

따라서 그 최적화는 루프가 실행되기 전에 하나의 비교를 수행하고 결과를 변수로 저장하는 것입니다.

누락 된 단어를 해석하려고합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.