내 주요 프로그램 논리의 여러 지점에서 호출되는 핫 함수에서 실행되므로 고도로 최적화 해야하는 Java 코드를 작업 중입니다. 이 코드의 일부에는 double변수에 10임의의 음이 아닌 값을 곱한 값 이 포함됩니다 int exponent. 곱한 값을 얻는 한 가지 빠른 방법 (편집 : 가장 빠른 것은 아니지만 아래 업데이트 2 참조) switch은 exponent다음 과 같습니다.
double multiplyByPowerOfTen(final double d, final int exponent) {
switch (exponent) {
case 0:
return d;
case 1:
return d*10;
case 2:
return d*100;
// ... same pattern
case 9:
return d*1000000000;
case 10:
return d*10000000000L;
// ... same pattern with long literals
case 18:
return d*1000000000000000000L;
default:
throw new ParseException("Unhandled power of ten " + power, 0);
}
}
위의 주석 처리 된 타원은 case int상수가 1 씩 증가 함을 나타내 므로 case위의 코드 스 니펫 에는 실제로 19 초가 있습니다. 나는 확실히 실제로 10의 모든 힘을 필요 여부 아니었다 때문에 case문 10을 통해 18, 나는이 완료 천만 작업에 시간을 비교하는 일부 microbenchmarks 실행 switch대 문 switch에서만 case이야 0통해 9과 ( exponent이하 9로 제한을 pared-down을 끊지 마십시오 switch). 나는 switch더 많은 case진술 로 더 길어질수록 실제로 더 빨리 실행 된다는 다소 놀라운 결과를 얻었 습니다.
case잠깐 동안 , 나는 더미 값을 반환하는 더 많은 s를 추가하려고 시도 했으며 약 22-27로 선언 된 스위치로 스위치를 더 빠르게 실행할 수 있음을 발견했습니다 case(코드가 실행되는 동안 더미 케이스가 실제로 맞지 않더라도) ). (다시 말해서, cases는 선행 case상수 를 씩 증가시켜 연속적인 방식으로 추가 되었습니다 1.) 이러한 실행 시간 차이는 그다지 중요하지 않습니다. 와 exponent사이 의 임의의 경우 더미 패딩 된 명령문은 1.49 초에서 1 천만 회의 실행과 패딩되지 않은 경우의 1.54 초의 1000 만회의 실행을 완료합니다. 버전, 실행 당 총 5ns의 총 절감. 따라서 패딩에 집착하는 것은 아닙니다.010switchswitch최적화 관점에서 노력할 가치가있는 진술. 그러나 나는 여전히 더 많은 것들이 추가 될 때 실행 switch속도가 느려지지 않거나 (또는 아마도 일정한 O (1) 시간을 유지하지 않는) 호기심과 반 직관적입니다 case.

이것은 무작위로 생성 된 exponent값 에 대한 다양한 제한으로 실행 한 결과 입니다. 나는 결과에 이르기까지 모든 방법을 포함하지 않았다 1에 대한 exponent제한을하지만, 곡선의 일반적인 형태는 12-17 경우 마크 주변의 능선, 그리고 18 ~ 28 인치 사이의 계곡으로, 동일하게 유지됩니다. 모든 테스트는 동일한 테스트 입력을 보장하기 위해 임의의 값에 대해 공유 컨테이너를 사용하여 JUnitBenchmarks에서 실행되었습니다. 또한 switch주문 관련 테스트 문제의 가능성을 시도하고 제거 하기 위해 가장 긴 문장부터 가장 짧은 것 까지 순서대로 테스트를 실행했습니다 . 누군가 가이 결과를 재현하려고한다면 github 저장소에 테스트 코드를 넣었습니다.
무슨 일이야? 내 아키텍처 또는 마이크로 벤치 마크 구성의 일부 미묘한 점이 있습니까? 또는 자바는 switch에서 실행하기 위해 조금 더 빨리 정말 18로 28 case그에서보다 범위 11까지 17?
업데이트 : 벤치마킹 라이브러리를 상당히 정리하고 / results에 텍스트 파일을 추가하여 더 넓은 범위의 가능한 exponent값을 출력했습니다 . 또한 테스트 코드에 Exceptionfrom 을 던지지 않는 옵션을 추가 default했지만 결과에 영향을 미치지 않는 것으로 보입니다.
업데이트 2 : 2009 년 xkcd 포럼 ( http://forums.xkcd.com/viewtopic.php?f=11&t=33524) 에서이 문제에 대한 좋은 토론을 발견했습니다 . OP의 사용에 대한 논의는 Array.binarySearch()위의 지수 패턴의 간단한 배열 기반 구현에 대한 아이디어를 제공했습니다. 항목이 무엇인지 알고 있기 때문에 이진 검색이 필요하지 않습니다 array. 사용 switch하는 제어 흐름 중 일부를 희생시키면서을 사용하는 것보다 약 3 배 빠르게 실행되는 것으로 보입니다 switch. 이 코드는 github 저장소에 추가되었습니다.
답변
다른 답변 에서 지적한 것처럼 사례 값이 인접하지 않기 때문에 (희소 한 것이 아니라) 다양한 테스트에 대해 생성 된 바이트 코드는 스위치 테이블 (바이트 코드 명령 tableswitch)을 사용합니다.
그러나 JIT가 작업을 시작하고 바이트 코드를 어셈블리로 컴파일하면 tableswitch명령에 항상 포인터 배열이 생성되지는 않습니다. 스위치 테이블이 lookupswitch( if/ else if구조 와 유사하게 ) 변환되는 경우도 있습니다.
JIT (hotspot JDK 1.7)에서 생성 된 어셈블리를 디 컴파일하면 17 개 이하의 경우에 if / else, 18 개 이상의 경우 (더 효율적인) 포인터 배열이 연속적으로 사용됩니다.
이 매직 넘버 18을 사용하는 이유는 MinJumpTableSizeJVM 플래그 의 기본값 (코드의 352 행) 으로 내려온 것 같습니다 .
핫스팟 컴파일러 목록에서 문제를 제기했으며 과거 테스트의 유산으로 보입니다 . 이 기본값 은 더 많은 벤치마킹이 수행 된 후 JDK 8에서 제거되었습니다 .
마지막으로 메소드가 너무 길어지면 (테스트에서 25 건 이상) 기본 JVM 설정으로 더 이상 인라인되지 않습니다.이 시점에서 성능이 저하 될 가능성이 가장 큽니다.
5 가지 경우의 디 컴파일 된 코드는 다음과 같습니다 (cmp / je / jg / jmp 명령어, if / goto의 어셈블리에 주목).
[Verified Entry Point]
# {method} 'multiplyByPowerOfTen' '(DI)D' in 'javaapplication4/Test1'
# parm0: xmm0:xmm0 = double
# parm1: rdx = int
# [sp+0x20] (sp of caller)
0x00000000024f0160: mov DWORD PTR [rsp-0x6000],eax
; {no_reloc}
0x00000000024f0167: push rbp
0x00000000024f0168: sub rsp,0x10 ;*synchronization entry
; - javaapplication4.Test1::multiplyByPowerOfTen@-1 (line 56)
0x00000000024f016c: cmp edx,0x3
0x00000000024f016f: je 0x00000000024f01c3
0x00000000024f0171: cmp edx,0x3
0x00000000024f0174: jg 0x00000000024f01a5
0x00000000024f0176: cmp edx,0x1
0x00000000024f0179: je 0x00000000024f019b
0x00000000024f017b: cmp edx,0x1
0x00000000024f017e: jg 0x00000000024f0191
0x00000000024f0180: test edx,edx
0x00000000024f0182: je 0x00000000024f01cb
0x00000000024f0184: mov ebp,edx
0x00000000024f0186: mov edx,0x17
0x00000000024f018b: call 0x00000000024c90a0 ; OopMap{off=48}
;*new ; - javaapplication4.Test1::multiplyByPowerOfTen@72 (line 83)
; {runtime_call}
0x00000000024f0190: int3 ;*new ; - javaapplication4.Test1::multiplyByPowerOfTen@72 (line 83)
0x00000000024f0191: mulsd xmm0,QWORD PTR [rip+0xffffffffffffffa7] # 0x00000000024f0140
;*dmul
; - javaapplication4.Test1::multiplyByPowerOfTen@52 (line 62)
; {section_word}
0x00000000024f0199: jmp 0x00000000024f01cb
0x00000000024f019b: mulsd xmm0,QWORD PTR [rip+0xffffffffffffff8d] # 0x00000000024f0130
;*dmul
; - javaapplication4.Test1::multiplyByPowerOfTen@46 (line 60)
; {section_word}
0x00000000024f01a3: jmp 0x00000000024f01cb
0x00000000024f01a5: cmp edx,0x5
0x00000000024f01a8: je 0x00000000024f01b9
0x00000000024f01aa: cmp edx,0x5
0x00000000024f01ad: jg 0x00000000024f0184 ;*tableswitch
; - javaapplication4.Test1::multiplyByPowerOfTen@1 (line 56)
0x00000000024f01af: mulsd xmm0,QWORD PTR [rip+0xffffffffffffff81] # 0x00000000024f0138
;*dmul
; - javaapplication4.Test1::multiplyByPowerOfTen@64 (line 66)
; {section_word}
0x00000000024f01b7: jmp 0x00000000024f01cb
0x00000000024f01b9: mulsd xmm0,QWORD PTR [rip+0xffffffffffffff67] # 0x00000000024f0128
;*dmul
; - javaapplication4.Test1::multiplyByPowerOfTen@70 (line 68)
; {section_word}
0x00000000024f01c1: jmp 0x00000000024f01cb
0x00000000024f01c3: mulsd xmm0,QWORD PTR [rip+0xffffffffffffff55] # 0x00000000024f0120
;*tableswitch
; - javaapplication4.Test1::multiplyByPowerOfTen@1 (line 56)
; {section_word}
0x00000000024f01cb: add rsp,0x10
0x00000000024f01cf: pop rbp
0x00000000024f01d0: test DWORD PTR [rip+0xfffffffffdf3fe2a],eax # 0x0000000000430000
; {poll_return}
0x00000000024f01d6: ret
18 건의 경우 어셈블리는 다음과 같이 보입니다 (사용되는 포인터 배열에 주목하고 모든 비교가 필요하지 jmp QWORD PTR [r8+r10*1]않음 : 올바른 곱셈으로 직접 점프)-성능 향상의 이유는 다음과 같습니다.
[Verified Entry Point]
# {method} 'multiplyByPowerOfTen' '(DI)D' in 'javaapplication4/Test1'
# parm0: xmm0:xmm0 = double
# parm1: rdx = int
# [sp+0x20] (sp of caller)
0x000000000287fe20: mov DWORD PTR [rsp-0x6000],eax
; {no_reloc}
0x000000000287fe27: push rbp
0x000000000287fe28: sub rsp,0x10 ;*synchronization entry
; - javaapplication4.Test1::multiplyByPowerOfTen@-1 (line 56)
0x000000000287fe2c: cmp edx,0x13
0x000000000287fe2f: jae 0x000000000287fe46
0x000000000287fe31: movsxd r10,edx
0x000000000287fe34: shl r10,0x3
0x000000000287fe38: movabs r8,0x287fd70 ; {section_word}
0x000000000287fe42: jmp QWORD PTR [r8+r10*1] ;*tableswitch
; - javaapplication4.Test1::multiplyByPowerOfTen@1 (line 56)
0x000000000287fe46: mov ebp,edx
0x000000000287fe48: mov edx,0x31
0x000000000287fe4d: xchg ax,ax
0x000000000287fe4f: call 0x00000000028590a0 ; OopMap{off=52}
;*new ; - javaapplication4.Test1::multiplyByPowerOfTen@202 (line 96)
; {runtime_call}
0x000000000287fe54: int3 ;*new ; - javaapplication4.Test1::multiplyByPowerOfTen@202 (line 96)
0x000000000287fe55: mulsd xmm0,QWORD PTR [rip+0xfffffffffffffe8b] # 0x000000000287fce8
;*dmul
; - javaapplication4.Test1::multiplyByPowerOfTen@194 (line 92)
; {section_word}
0x000000000287fe5d: jmp 0x000000000287ff16
0x000000000287fe62: mulsd xmm0,QWORD PTR [rip+0xfffffffffffffe86] # 0x000000000287fcf0
;*dmul
; - javaapplication4.Test1::multiplyByPowerOfTen@188 (line 90)
; {section_word}
0x000000000287fe6a: jmp 0x000000000287ff16
0x000000000287fe6f: mulsd xmm0,QWORD PTR [rip+0xfffffffffffffe81] # 0x000000000287fcf8
;*dmul
; - javaapplication4.Test1::multiplyByPowerOfTen@182 (line 88)
; {section_word}
0x000000000287fe77: jmp 0x000000000287ff16
0x000000000287fe7c: mulsd xmm0,QWORD PTR [rip+0xfffffffffffffe7c] # 0x000000000287fd00
;*dmul
; - javaapplication4.Test1::multiplyByPowerOfTen@176 (line 86)
; {section_word}
0x000000000287fe84: jmp 0x000000000287ff16
0x000000000287fe89: mulsd xmm0,QWORD PTR [rip+0xfffffffffffffe77] # 0x000000000287fd08
;*dmul
; - javaapplication4.Test1::multiplyByPowerOfTen@170 (line 84)
; {section_word}
0x000000000287fe91: jmp 0x000000000287ff16
0x000000000287fe96: mulsd xmm0,QWORD PTR [rip+0xfffffffffffffe72] # 0x000000000287fd10
;*dmul
; - javaapplication4.Test1::multiplyByPowerOfTen@164 (line 82)
; {section_word}
0x000000000287fe9e: jmp 0x000000000287ff16
0x000000000287fea0: mulsd xmm0,QWORD PTR [rip+0xfffffffffffffe70] # 0x000000000287fd18
;*dmul
; - javaapplication4.Test1::multiplyByPowerOfTen@158 (line 80)
; {section_word}
0x000000000287fea8: jmp 0x000000000287ff16
0x000000000287feaa: mulsd xmm0,QWORD PTR [rip+0xfffffffffffffe6e] # 0x000000000287fd20
;*dmul
; - javaapplication4.Test1::multiplyByPowerOfTen@152 (line 78)
; {section_word}
0x000000000287feb2: jmp 0x000000000287ff16
0x000000000287feb4: mulsd xmm0,QWORD PTR [rip+0xfffffffffffffe24] # 0x000000000287fce0
;*dmul
; - javaapplication4.Test1::multiplyByPowerOfTen@146 (line 76)
; {section_word}
0x000000000287febc: jmp 0x000000000287ff16
0x000000000287febe: mulsd xmm0,QWORD PTR [rip+0xfffffffffffffe6a] # 0x000000000287fd30
;*dmul
; - javaapplication4.Test1::multiplyByPowerOfTen@140 (line 74)
; {section_word}
0x000000000287fec6: jmp 0x000000000287ff16
0x000000000287fec8: mulsd xmm0,QWORD PTR [rip+0xfffffffffffffe68] # 0x000000000287fd38
;*dmul
; - javaapplication4.Test1::multiplyByPowerOfTen@134 (line 72)
; {section_word}
0x000000000287fed0: jmp 0x000000000287ff16
0x000000000287fed2: mulsd xmm0,QWORD PTR [rip+0xfffffffffffffe66] # 0x000000000287fd40
;*dmul
; - javaapplication4.Test1::multiplyByPowerOfTen@128 (line 70)
; {section_word}
0x000000000287feda: jmp 0x000000000287ff16
0x000000000287fedc: mulsd xmm0,QWORD PTR [rip+0xfffffffffffffe64] # 0x000000000287fd48
;*dmul
; - javaapplication4.Test1::multiplyByPowerOfTen@122 (line 68)
; {section_word}
0x000000000287fee4: jmp 0x000000000287ff16
0x000000000287fee6: mulsd xmm0,QWORD PTR [rip+0xfffffffffffffe62] # 0x000000000287fd50
;*dmul
; - javaapplication4.Test1::multiplyByPowerOfTen@116 (line 66)
; {section_word}
0x000000000287feee: jmp 0x000000000287ff16
0x000000000287fef0: mulsd xmm0,QWORD PTR [rip+0xfffffffffffffe60] # 0x000000000287fd58
;*dmul
; - javaapplication4.Test1::multiplyByPowerOfTen@110 (line 64)
; {section_word}
0x000000000287fef8: jmp 0x000000000287ff16
0x000000000287fefa: mulsd xmm0,QWORD PTR [rip+0xfffffffffffffe5e] # 0x000000000287fd60
;*dmul
; - javaapplication4.Test1::multiplyByPowerOfTen@104 (line 62)
; {section_word}
0x000000000287ff02: jmp 0x000000000287ff16
0x000000000287ff04: mulsd xmm0,QWORD PTR [rip+0xfffffffffffffe5c] # 0x000000000287fd68
;*dmul
; - javaapplication4.Test1::multiplyByPowerOfTen@98 (line 60)
; {section_word}
0x000000000287ff0c: jmp 0x000000000287ff16
0x000000000287ff0e: mulsd xmm0,QWORD PTR [rip+0xfffffffffffffe12] # 0x000000000287fd28
;*tableswitch
; - javaapplication4.Test1::multiplyByPowerOfTen@1 (line 56)
; {section_word}
0x000000000287ff16: add rsp,0x10
0x000000000287ff1a: pop rbp
0x000000000287ff1b: test DWORD PTR [rip+0xfffffffffd9b00df],eax # 0x0000000000230000
; {poll_return}
0x000000000287ff21: ret
마지막 으로 @cHao가 발견 한 것처럼movapd xmm0,xmm1 코드 중간에 나타나는 추가 항목 을 제외하고 30 건 (아래)의 어셈블리는 18 건과 유사 하지만 성능 저하의 가장 큰 이유는 방법이 너무 큽니다. 기본 JVM 설정으로 인라인되기를 바랍니다.
[Verified Entry Point]
# {method} 'multiplyByPowerOfTen' '(DI)D' in 'javaapplication4/Test1'
# parm0: xmm0:xmm0 = double
# parm1: rdx = int
# [sp+0x20] (sp of caller)
0x0000000002524560: mov DWORD PTR [rsp-0x6000],eax
; {no_reloc}
0x0000000002524567: push rbp
0x0000000002524568: sub rsp,0x10 ;*synchronization entry
; - javaapplication4.Test1::multiplyByPowerOfTen@-1 (line 56)
0x000000000252456c: movapd xmm1,xmm0
0x0000000002524570: cmp edx,0x1f
0x0000000002524573: jae 0x0000000002524592 ;*tableswitch
; - javaapplication4.Test1::multiplyByPowerOfTen@1 (line 56)
0x0000000002524575: movsxd r10,edx
0x0000000002524578: shl r10,0x3
0x000000000252457c: mulsd xmm0,QWORD PTR [rip+0xfffffffffffffe3c] # 0x00000000025243c0
;*dmul
; - javaapplication4.Test1::multiplyByPowerOfTen@364 (line 118)
; {section_word}
0x0000000002524584: movabs r8,0x2524450 ; {section_word}
0x000000000252458e: jmp QWORD PTR [r8+r10*1] ;*tableswitch
; - javaapplication4.Test1::multiplyByPowerOfTen@1 (line 56)
0x0000000002524592: mov ebp,edx
0x0000000002524594: mov edx,0x31
0x0000000002524599: xchg ax,ax
0x000000000252459b: call 0x00000000024f90a0 ; OopMap{off=64}
;*new ; - javaapplication4.Test1::multiplyByPowerOfTen@370 (line 120)
; {runtime_call}
0x00000000025245a0: int3 ;*new ; - javaapplication4.Test1::multiplyByPowerOfTen@370 (line 120)
0x00000000025245a1: mulsd xmm0,QWORD PTR [rip+0xfffffffffffffe27] # 0x00000000025243d0
;*dmul
; - javaapplication4.Test1::multiplyByPowerOfTen@358 (line 116)
; {section_word}
0x00000000025245a9: jmp 0x0000000002524744
0x00000000025245ae: mulsd xmm0,QWORD PTR [rip+0xfffffffffffffe22] # 0x00000000025243d8
;*dmul
; - javaapplication4.Test1::multiplyByPowerOfTen@348 (line 114)
; {section_word}
0x00000000025245b6: jmp 0x0000000002524744
0x00000000025245bb: mulsd xmm0,QWORD PTR [rip+0xfffffffffffffe1d] # 0x00000000025243e0
;*dmul
; - javaapplication4.Test1::multiplyByPowerOfTen@338 (line 112)
; {section_word}
0x00000000025245c3: jmp 0x0000000002524744
0x00000000025245c8: mulsd xmm0,QWORD PTR [rip+0xfffffffffffffe18] # 0x00000000025243e8
;*dmul
; - javaapplication4.Test1::multiplyByPowerOfTen@328 (line 110)
; {section_word}
0x00000000025245d0: jmp 0x0000000002524744
0x00000000025245d5: mulsd xmm0,QWORD PTR [rip+0xfffffffffffffe13] # 0x00000000025243f0
;*dmul
; - javaapplication4.Test1::multiplyByPowerOfTen@318 (line 108)
; {section_word}
0x00000000025245dd: jmp 0x0000000002524744
0x00000000025245e2: mulsd xmm0,QWORD PTR [rip+0xfffffffffffffe0e] # 0x00000000025243f8
;*dmul
; - javaapplication4.Test1::multiplyByPowerOfTen@308 (line 106)
; {section_word}
0x00000000025245ea: jmp 0x0000000002524744
0x00000000025245ef: mulsd xmm0,QWORD PTR [rip+0xfffffffffffffe09] # 0x0000000002524400
;*dmul
; - javaapplication4.Test1::multiplyByPowerOfTen@298 (line 104)
; {section_word}
0x00000000025245f7: jmp 0x0000000002524744
0x00000000025245fc: mulsd xmm0,QWORD PTR [rip+0xfffffffffffffe04] # 0x0000000002524408
;*dmul
; - javaapplication4.Test1::multiplyByPowerOfTen@288 (line 102)
; {section_word}
0x0000000002524604: jmp 0x0000000002524744
0x0000000002524609: mulsd xmm0,QWORD PTR [rip+0xfffffffffffffdff] # 0x0000000002524410
;*dmul
; - javaapplication4.Test1::multiplyByPowerOfTen@278 (line 100)
; {section_word}
0x0000000002524611: jmp 0x0000000002524744
0x0000000002524616: mulsd xmm0,QWORD PTR [rip+0xfffffffffffffdfa] # 0x0000000002524418
;*dmul
; - javaapplication4.Test1::multiplyByPowerOfTen@268 (line 98)
; {section_word}
0x000000000252461e: jmp 0x0000000002524744
0x0000000002524623: mulsd xmm0,QWORD PTR [rip+0xfffffffffffffd9d] # 0x00000000025243c8
;*dmul
; - javaapplication4.Test1::multiplyByPowerOfTen@258 (line 96)
; {section_word}
0x000000000252462b: jmp 0x0000000002524744
0x0000000002524630: movapd xmm0,xmm1
0x0000000002524634: mulsd xmm0,QWORD PTR [rip+0xfffffffffffffe0c] # 0x0000000002524448
;*dmul
; - javaapplication4.Test1::multiplyByPowerOfTen@242 (line 92)
; {section_word}
0x000000000252463c: jmp 0x0000000002524744
0x0000000002524641: movapd xmm0,xmm1
0x0000000002524645: mulsd xmm0,QWORD PTR [rip+0xfffffffffffffddb] # 0x0000000002524428
;*dmul
; - javaapplication4.Test1::multiplyByPowerOfTen@236 (line 90)
; {section_word}
0x000000000252464d: jmp 0x0000000002524744
0x0000000002524652: movapd xmm0,xmm1
0x0000000002524656: mulsd xmm0,QWORD PTR [rip+0xfffffffffffffdd2] # 0x0000000002524430
;*dmul
; - javaapplication4.Test1::multiplyByPowerOfTen@230 (line 88)
; {section_word}
0x000000000252465e: jmp 0x0000000002524744
0x0000000002524663: movapd xmm0,xmm1
0x0000000002524667: mulsd xmm0,QWORD PTR [rip+0xfffffffffffffdc9] # 0x0000000002524438
;*dmul
; - javaapplication4.Test1::multiplyByPowerOfTen@224 (line 86)
; {section_word}
[etc.]
0x0000000002524744: add rsp,0x10
0x0000000002524748: pop rbp
0x0000000002524749: test DWORD PTR [rip+0xfffffffffde1b8b1],eax # 0x0000000000340000
; {poll_return}
0x000000000252474f: ret
답변
사례 값이 좁은 범위에 배치되면 스위치-사례가 더 빠릅니다.
case 1:
case 2:
case 3:
..
..
case n:
이 경우 컴파일러는 switch 문의 모든 경우 구간에 대한 비교를 수행하지 않아도됩니다. 컴파일러는 다른 레그에서 수행 할 동작의 주소를 포함하는 점프 테이블을 만듭니다. 스위치가 수행되는 값은의 인덱스로 변환되도록 조작됩니다 jump table. 이 구현에서 switch 문에 걸리는 시간은 동등한 if-else-if 문에 걸리는 시간보다 훨씬 짧습니다. 또한 switch 문에 걸리는 시간은 switch 문에있는 케이스 레그 수와 무관합니다.
Wikipedia 에서 컴파일 섹션의 스위치 문 에 대해 설명 합니다.
입력 값의 범위가 ‘소규모’로 식별 할 수 있고 약간의 차이 만있는 경우 최적화 프로그램을 통합하는 일부 컴파일러는 실제로 일련의 조건부 명령어 대신 분기 테이블 또는 인덱싱 된 함수 포인터 배열로 switch 문을 구현할 수 있습니다. 이를 통해 switch 문은 비교 목록을 거치지 않고 실행할 분기를 즉시 결정할 수 있습니다.
답변
답은 바이트 코드에 있습니다.
SwitchTest10.java
public class SwitchTest10 {
public static void main(String[] args) {
int n = 0;
switcher(n);
}
public static void switcher(int n) {
switch(n) {
case 0: System.out.println(0);
break;
case 1: System.out.println(1);
break;
case 2: System.out.println(2);
break;
case 3: System.out.println(3);
break;
case 4: System.out.println(4);
break;
case 5: System.out.println(5);
break;
case 6: System.out.println(6);
break;
case 7: System.out.println(7);
break;
case 8: System.out.println(8);
break;
case 9: System.out.println(9);
break;
case 10: System.out.println(10);
break;
default: System.out.println("test");
}
}
}
해당 바이트 코드; 관련 부품 만 표시 :
public static void switcher(int);
Code:
0: iload_0
1: tableswitch{ //0 to 10
0: 60;
1: 70;
2: 80;
3: 90;
4: 100;
5: 110;
6: 120;
7: 131;
8: 142;
9: 153;
10: 164;
default: 175 }
SwitchTest22.java :
public class SwitchTest22 {
public static void main(String[] args) {
int n = 0;
switcher(n);
}
public static void switcher(int n) {
switch(n) {
case 0: System.out.println(0);
break;
case 1: System.out.println(1);
break;
case 2: System.out.println(2);
break;
case 3: System.out.println(3);
break;
case 4: System.out.println(4);
break;
case 5: System.out.println(5);
break;
case 6: System.out.println(6);
break;
case 7: System.out.println(7);
break;
case 8: System.out.println(8);
break;
case 9: System.out.println(9);
break;
case 100: System.out.println(10);
break;
case 110: System.out.println(10);
break;
case 120: System.out.println(10);
break;
case 130: System.out.println(10);
break;
case 140: System.out.println(10);
break;
case 150: System.out.println(10);
break;
case 160: System.out.println(10);
break;
case 170: System.out.println(10);
break;
case 180: System.out.println(10);
break;
case 190: System.out.println(10);
break;
case 200: System.out.println(10);
break;
case 210: System.out.println(10);
break;
case 220: System.out.println(10);
break;
default: System.out.println("test");
}
}
}
해당 바이트 코드; 다시, 관련 부품 만 표시 :
public static void switcher(int);
Code:
0: iload_0
1: lookupswitch{ //23
0: 196;
1: 206;
2: 216;
3: 226;
4: 236;
5: 246;
6: 256;
7: 267;
8: 278;
9: 289;
100: 300;
110: 311;
120: 322;
130: 333;
140: 344;
150: 355;
160: 366;
170: 377;
180: 388;
190: 399;
200: 410;
210: 421;
220: 432;
default: 443 }
첫 번째 경우 범위가 좁은 경우 컴파일 된 바이트 코드는를 사용합니다 tableswitch. 두 번째 경우, 컴파일 된 바이트 코드는를 사용합니다 lookupswitch.
에서 tableswitch, 스택의 상단에있는 정수 값은 지점 / 점프 대상을 찾기 위해, 테이블에 인덱스로 사용된다. 그런 다음이 점프 / 분기가 즉시 수행됩니다. 따라서 이것은 O(1)작업입니다.
A lookupswitch가 더 복잡합니다. 이 경우 올바른 키를 찾을 때까지 정수 값을 테이블의 모든 키와 비교해야합니다. 키가 발견되면 분기 / 점프 대상 (이 키가 매핑 된)이 점프에 사용됩니다. 사용되는 테이블 lookupswitch이 정렬되고 이진 검색 알고리즘을 사용하여 올바른 키를 찾을 수 있습니다. 이진 검색의 성능은 O(log n), 전체 과정은 또한 O(log n)점프가 여전히 있기 때문에 O(1). 따라서 희소 범위의 경우 성능이 저하되는 이유는 테이블에 직접 색인을 작성할 수 없기 때문에 올바른 키를 먼저 찾아야하기 때문입니다.
스파 스 값이 있고 tableswitch사용할 수있는 경우 테이블에는 기본적으로 default옵션 을 가리키는 더미 항목이 포함 됩니다. 예를 들어의 마지막 항목 SwitchTest10.java이 21대신에 있다고 가정하면 10다음과 같은 결과가 나타납니다.
public static void switcher(int);
Code:
0: iload_0
1: tableswitch{ //0 to 21
0: 104;
1: 114;
2: 124;
3: 134;
4: 144;
5: 154;
6: 164;
7: 175;
8: 186;
9: 197;
10: 219;
11: 219;
12: 219;
13: 219;
14: 219;
15: 219;
16: 219;
17: 219;
18: 219;
19: 219;
20: 219;
21: 208;
default: 219 }
따라서 컴파일러는 기본적으로 간격 사이의 더미 항목을 포함하는 거대한 테이블을 작성하여 default명령 의 분기 대상을 가리 킵니다 . 이 없더라도 스위치 블록 뒤 의 default명령 을 가리키는 항목이 포함됩니다 . 몇 가지 기본 테스트를 수행했으며 마지막 인덱스와 이전 인덱스 사이의 간격 9이보다 크면을 대신 35사용 lookupswitch합니다 tableswitch.
switch명령문 의 동작은 Java Virtual Machine Specification (§3.10)에 정의되어 있습니다 .
스위치의 경우가 드문 경우, 테이블 스위치 명령의 테이블 표현은 공간 측면에서 비효율적입니다. lookupswitch 명령이 대신 사용될 수 있습니다. lookupswitch 명령어는 테이블의 대상 오프셋과 int 키 (케이스 레이블 값)를 쌍으로합니다. 조회 스위치 명령이 실행될 때 스위치 식의 값이 테이블의 키와 비교됩니다. 키 중 하나가 표현식의 값과 일치하면 연관된 대상 오프셋에서 실행이 계속됩니다. 일치하는 키가 없으면 기본 대상에서 실행이 계속됩니다. […]
답변
질문에 이미 답변이되었으므로 여기에 몇 가지 팁이 있습니다. 사용하다
private static final double[] mul={1d, 10d...};
static double multiplyByPowerOfTen(final double d, final int exponent) {
if (exponent<0 || exponent>=mul.length) throw new ParseException();//or just leave the IOOBE be
return mul[exponent]*d;
}
이 코드는 훨씬 적은 IC (명령 캐시)를 사용하며 항상 인라인됩니다. 코드가 핫하면 어레이는 L1 데이터 캐시에있게됩니다. 룩업 테이블은 거의 항상 승리입니다. (마이크로 벤치 마크 : D)
편집 : 방법을 핫 인라인으로 만들려면 빠른 경로가 아닌 경로 throw new ParseException()를 최소 길이로 생각하거나 별도의 정적 메서드로 이동하여 최소 길이로 만드십시오. 그것은 throw new ParseException("Unhandled power of ten " + power, 0);해석하기 쉬운 코드에 대한 많은 인라이닝 예산을 먹는 약한 아이디어입니다. 문자열 연결은 바이트 코드에서 매우 장황합니다. 더 많은 정보와 ArrayList가 있는 실제 사례