스위치 문의 사례 순서가 성능에 영향을 미칩니까?
나는 있습니다switch사례 프로그램:
오름차순 스위치 케이스:
int main()
{
int a, sc = 1;
switch (sc)
{
case 1:
a = 1;
break;
case 2:
a = 2;
break;
}
}
코드 어셈블리:
main:
push rbp
mov rbp, rsp
mov DWORD PTR [rbp-4], 1
mov eax, DWORD PTR [rbp-4]
cmp eax, 1
je .L3
cmp eax, 2
je .L4
jmp .L2
.L3:
mov DWORD PTR [rbp-8], 1
jmp .L2
.L4:
mov DWORD PTR [rbp-8], 2
nop
.L2:
mov eax, 0
pop rbp
ret
내림차순 전환 사례:
int main()
{
int a, sc = 1;
switch (sc)
{
case 2:
a = 1;
break;
case 1:
a = 2;
break;
}
}
코드 어셈블리:
main:
push rbp
mov rbp, rsp
mov DWORD PTR [rbp-4], 1
mov eax, DWORD PTR [rbp-4]
cmp eax, 1
je .L3
cmp eax, 2
jne .L2
mov DWORD PTR [rbp-8], 1
jmp .L2
.L3:
mov DWORD PTR [rbp-8], 2
nop
.L2:
mov eax, 0
pop rbp
ret
여기서 오름차순 사례는 내림차순보다 더 많은 어셈블리를 생성했습니다.
그러면 스위치 케이스 수가 많아지면 케이스 수가 성능에 영향을 미치는가요?
최적화되지 않은 코드를 보고 있으므로 성능을 위해 코드를 연구하는 것은 그다지 의미가 없습니다.예제에 최적화된 코드를 보면 비교가 전혀 되지 않는다는 것을 알게 될 것입니다!옵티마이저는 스위치 변수가sc항상 가치가 있습니다.1그래서 그것은 도달할 수 없는 것을 제거합니다.case 2.
최적화 또한최화도변수가는을 합니다.a된 후에는▁isn▁the다▁removes니제합에 있는 코드를 제거합니다.case 1 한또, 떠는것을 남겨두는 것.main()그리고 조작하는 프롤로그/에필로그 합니다.rbp그 레지스터는 사용되지 않기 때문입니다.
따라서 최적화된 코드는 두 버전 모두 동일하게 됩니다.main()함수:
main:
xor eax, eax
ret
간단히 말해서, 질문의 코드에 대해서는, 당신이 어떤 순서를 두었는지는 중요하지 않습니다.case해당 코드는 전혀 생성되지 않기 때문에 문을 클릭합니다.
윌더case코드가 실제로 생성되고 사용되는 보다 실제 사례에서 주문 문제?아마 아닐 것입니다.최적화되지 않은 생성된 코드에서도 두 버전 모두 두 버전에 대해 테스트합니다.case순서의 값, "" " " " " " " " 1에 다에는음그는.2소스 코드의 순서에 관계없이.컴파일러가 최적화되지 않은 코드에서도 정렬을 수행하고 있는 것이 분명합니다.
글렌과 룬딘의 발언을 주의하세요: 순서.case섹션은 두 예제 간의 유일한 변경 사항이 아니며, 실제 코드도 다릅니다.중 은 그 하 나 값 다 같 설 일 값 합 니 치 다 과 된 정 이 에 설정된 합니다.a하지만 다른 쪽에서는 그렇지 않습니다.
컴파일러는 다양한 전략을 사용합니다.switch/case실제 사용된 값에 따라 문장이 달라집니다.이러한 예와 같이 일련의 비교를 사용하거나 점프 표를 사용할 수 있습니다.생성된 코드를 연구하는 것은 흥미로울 수 있지만, 성능이 중요한 경우에는 최적화 설정을 확인하고 실제 상황에서 테스트하십시오.
컴파일러 최적화:switch진술이 까다롭습니다.물론 최적화를 활성화해야 합니다(예: 코드를 컴파일할 때는gcc -O2 -fverbose-asm -SGCC를 사용하여 생성된 내부를 확인합니다..s어셈블리 파일).BTW 두 예 모두에서 Debian/Sid/x86-64의 my GCC 7은 다음과 같이 간단히 제공합니다.
.type main, @function
main:
.LFB0:
.cfi_startproc
# rsp.c:13: }
xorl %eax, %eax #
ret
.cfi_endproc
(의 흔적이 .)switch생성된 코드에서)
컴파일러가 최적화할 수 있는 방법을 이해해야 하는 경우switch그 주제에 대한 논문들이 있습니다, 이것과 같은.
스위치 케이스 수가 많으면 케이스 순서가 성능에 영향을 줍니까?
일반적으로 일부 최적화 컴파일러를 사용하고 있으며 최적화를 요청하는 경우에는 그렇지 않습니다.이 항목도 참조하십시오.
만약 그것이 당신에게 매우 중요하다면(그러나 당신의 컴파일러에게 마이크로 최적화를 맡겨서는 안 된다!), 당신은 벤치마크하고, 프로파일링하고, 생성된 어셈블리어 코드를 연구해야 합니다.BTW, 캐시 누락 및 레지스터 할당은 다음의 순서보다 훨씬 더 중요할 수 있습니다.case-그래서 저는 당신이 전혀 신경쓰지 않는 것이 좋다고 생각합니다.최근 컴퓨터의 대략적인 타이밍 추정치를 염두에 두십시오.집어넣어요cases는 가장 읽기 쉬운 순서로 되어 있습니다(같은 소스 코드로 작업하는 다음 개발자용).스레드 코드에 대해서도 읽어 보십시오.목표(성능 관련)로 재주문해야 하는 이유가 있는 경우case-s(매우 가능성이 낮고 일생에 한 번은 발생해야 함)는 그 이유를 설명하는 좋은 댓글을 작성합니다.
성능에 관심이 많다면 벤치마크 및 프로파일링을 수행하고 적절한 컴파일러를 선택하여 관련 최적화 옵션과 함께 사용하십시오.여러 가지 다른 최적화 설정(및 여러 컴파일러)을 실험합니다.추가할 수 있습니다(추가).-O2또는-O3) 컴파일 및 링크를 고려할 수 있습니다.-flto -O2링크 시간 최적화 등을 활성화합니다.프로파일 기반 최적화도 필요할 수 있습니다.
그나저나, 많은 컴파일러들은 거대한 자유 소프트웨어 프로젝트(특히 GCC와 Clang)입니다.성능에 대해 그렇게 신경을 쓴다면 컴파일러에 패치를 적용하고 소스 코드를 포킹하거나 일부 플러그인을 GCC 또는 일부 GCC MELT 확장에 추가하여 최적화 패스를 추가하여 확장할 수 있습니다.이를 위해서는 몇 달 또는 몇 년의 작업이 필요합니다(특히 해당 컴파일러의 내부 표현 및 구성을 이해하는 데).
(개발 비용을 고려하는 것을 잊지 마십시오. 대부분의 경우 비용이 훨씬 더 많이 듭니다.)
대부분의 경우 레이블이 연속적인 경우 컴파일러는 비교보다는 점프 테이블을 사용하기 위해 스위치 문을 처리하는 경우가 많습니다.컴파일러가 사용할 계산 점프의 형태(있는 경우)를 결정하는 정확한 수단은 구현마다 다릅니다.때때로 코드를 을 향상시킬 수 대소문자하고 대소문자 방식으로 ).case 0:; case 1:; case 2:; case 3:; default:컴파일러가 피연산자를 12개와 비교하고, 그보다 작으면 12개 항목의 점프 테이블을 사용할 수 있습니다.이러한 경우를 생략하면 컴파일러가 차이를 8과 비교하기 전에 4를 뺀 다음 8개 항목 테이블을 사용할 수 있습니다.
스위치 문을 최적화하려고 시도할 때의 한 가지 어려움은 컴파일러가 일반적으로 특정 입력이 주어졌을 때 다른 접근 방식의 성능이 어떻게 달라질지 프로그래머보다 더 잘 알고 있지만, 프로그래머는 컴파일러보다 프로그램이 어떤 입력의 분포를 받을지 더 잘 알 수 있습니다.다음과 같은 것이 주어집니다.
if (x==0)
y++;
else switch(x)
{
...
}
"스마트" 컴파일러는 코드를 다음으로 변경하는 것을 인식할 수 있습니다.
switch(x)
{
case 0:
y++;
break;
...
}
모든 경우에서 비교를 제거할 수 있습니다.x 경우 됩니다.x0입니다. 만약x대부분 0이 아닌 경우, 그것은 좋은 거래가 될 것입니다. 만약에.x은 나쁜 있습니다. 99.9%의 비율을 차지하지만, 그것은 나쁜 거래가 될 수도 있습니다.컴파일러 작성자마다 전자와 같은 구조를 후자에 최적화하려는 정도가 다릅니다.
성능은 전체 사례 수에 따라 결정되는 것이 아니라 특정 데이터 세트의 분기 누락 수에 따라 결정됩니다.그리고 그것은 결국 실제 데이터와 컴파일러가 스위치를 구현하기로 선택한 방법에 크게 의존합니다. (디스패치 테이블, 연결된 조건, 조건 트리) C에서 이것을 제어할 수 있을지 조차 확실하지 않습니다.
스위치 문은 일반적으로 단순한 비교가 아닌 점프 테이블을 통해 컴파일됩니다.
따라서 대/소문자를 반복해도 성능이 저하되지 않습니다.
그러나 실행 흐름이 다음 사례로 이동하여 코드가 중복되지 않도록 하려면 더 많은 사례를 연속적으로 순서대로 보관하고 일부 항목에서는 중단/반환을 사용하지 않는 것이 유용할 수 있습니다.
와 소문자의 때number한 사건에서 다른 사건으로, 예를 들면.case 10:그리고.case 200000:컴파일러는 점프 테이블을 생성하지 않을 것입니다. 왜냐하면 그것은 거의 모든 약 200,000개의 엔트리를 포인터로 채워야 하기 때문입니다.default:이 경우 비교를 사용합니다.
질문은 매우 간단합니다. 코드가 동일하지 않으므로 동일한 어셈블리를 생성하지 않습니다.최적화된 코드는 개별 문뿐만 아니라 그 주변의 모든 것에 달려 있습니다.이 경우 최적화를 쉽게 설명할 수 있습니다.
첫 번째 예제에서 사례 1은 a=1이고 사례 2는 a=2입니다.컴파일러는 이를 최적화하여 이 두 경우에 대해 단일 문인 a=sc를 설정할 수 있습니다.
두 번째 예제에서 사례 1은 a=2이고 사례 2는 a=1입니다.컴파일러는 더 이상 바로 가기를 사용할 수 없으므로 두 경우 모두에 대해 a=1 또는 a=2를 명시적으로 설정해야 합니다.물론 더 많은 코드가 필요합니다.
단순히 첫 번째 예를 들어 사례의 순서와 조건부 코드를 바꾼 경우 동일한 어셈블리를 받아야 합니다.
코드를 사용하여 이 최적화를 테스트할 수 있습니다.
int main()
{
int a, sc = 1;
switch (sc)
{
case 1:
case 2:
a = sc;
break;
}
}
정확하게 동일한 어셈블리를 제공해야 합니다.
덧붙여서, 당신의 테스트 코드는 sc가 실제로 읽혔다고 가정합니다.대부분의 최신 최적화 컴파일러는 sc가 할당과 스위치 문 사이에서 변하지 않음을 발견할 수 있으며, 판독 sc를 상수 값 1로 대체할 수 있습니다.추가 최적화를 수행하면 스위치 문의 중복 분기가 제거되고, 실제로는 변경되지 않으므로 할당도 최적화될 수 있습니다.그리고 변수 a의 관점에서 컴파일러는 a가 다른 곳에서 읽히지 않는다는 것을 발견하고 코드에서 변수를 완전히 제거할 수도 있습니다.
를 둘 다 .volatile다행히 컴파일러는 당신이 예상했던 방식으로 구현한 것처럼 보이지만, 최적화를 설정했을 때는 이것을 절대적으로 기대할 수 없습니다.
어셈블리 코드를 비교하기 전에 컴파일러에 대한 최적화를 활성화해야 하지만, 문제는 컴파일러가 아무런 부작용이 없기 때문에 함수에서 모든 것을 제거할 수 있도록 변수가 컴파일러에 알려져 있다는 것입니다.
이 예제에서는 스위치 문에서 사례 순서를 변경하더라도 최적화가 활성화된 경우 GCC 및 대부분의 다른 컴파일러가 사례 순서를 변경합니다.외부 함수를 사용하여 실행 시에만 값을 알 수 있도록 했지만 사용할 수도 있었습니다.rand예를들면.
또한 사례를 더 추가할 때 컴파일러는 조건부 점프를 함수의 주소가 포함된 테이블로 대체할 수 있으며 여기에서 볼 수 있듯이 GCC에 의해 다시 정렬됩니다.
언급URL : https://stackoverflow.com/questions/47089878/does-the-order-of-cases-in-a-switch-statement-affect-performance
'programing' 카테고리의 다른 글
| jquery를 사용하여 div에서 스타일 속성 추가 및 제거 (0) | 2023.08.11 |
|---|---|
| Excel VBA를 사용하여 코드 128 바코드 생성 (0) | 2023.08.11 |
| 파이썬에 'string.split()'의 생성기 버전이 있습니까? (0) | 2023.08.11 |
| 검사 제약 조건에서 날짜 사용, Oracle (0) | 2023.08.11 |
| 명명된 매개 변수 JdbcTemplate 대 JdbcTemplate (0) | 2023.08.11 |