내 주요 프로그램 논리의 여러 지점에서 호출되는 핫 함수에서 실행되므로 고도로 최적화 해야하는 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
(코드가 실행되는 동안 더미 케이스가 실제로 맞지 않더라도) ). (다시 말해서, case
s는 선행 case
상수 를 씩 증가시켜 연속적인 방식으로 추가 되었습니다 1
.) 이러한 실행 시간 차이는 그다지 중요하지 않습니다. 와 exponent
사이 의 임의의 경우 더미 패딩 된 명령문은 1.49 초에서 1 천만 회의 실행과 패딩되지 않은 경우의 1.54 초의 1000 만회의 실행을 완료합니다. 버전, 실행 당 총 5ns의 총 절감. 따라서 패딩에 집착하는 것은 아닙니다.0
10
switch
switch
최적화 관점에서 노력할 가치가있는 진술. 그러나 나는 여전히 더 많은 것들이 추가 될 때 실행 switch
속도가 느려지지 않거나 (또는 아마도 일정한 O (1) 시간을 유지하지 않는) 호기심과 반 직관적입니다 case
.
이것은 무작위로 생성 된 exponent
값 에 대한 다양한 제한으로 실행 한 결과 입니다. 나는 결과에 이르기까지 모든 방법을 포함하지 않았다 1
에 대한 exponent
제한을하지만, 곡선의 일반적인 형태는 12-17 경우 마크 주변의 능선, 그리고 18 ~ 28 인치 사이의 계곡으로, 동일하게 유지됩니다. 모든 테스트는 동일한 테스트 입력을 보장하기 위해 임의의 값에 대해 공유 컨테이너를 사용하여 JUnitBenchmarks에서 실행되었습니다. 또한 switch
주문 관련 테스트 문제의 가능성을 시도하고 제거 하기 위해 가장 긴 문장부터 가장 짧은 것 까지 순서대로 테스트를 실행했습니다 . 누군가 가이 결과를 재현하려고한다면 github 저장소에 테스트 코드를 넣었습니다.
무슨 일이야? 내 아키텍처 또는 마이크로 벤치 마크 구성의 일부 미묘한 점이 있습니까? 또는 자바는 switch
에서 실행하기 위해 조금 더 빨리 정말 18
로 28
case
그에서보다 범위 11
까지 17
?
업데이트 : 벤치마킹 라이브러리를 상당히 정리하고 / results에 텍스트 파일을 추가하여 더 넓은 범위의 가능한 exponent
값을 출력했습니다 . 또한 테스트 코드에 Exception
from 을 던지지 않는 옵션을 추가 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을 사용하는 이유는 MinJumpTableSize
JVM 플래그 의 기본값 (코드의 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가 있는 실제 사례