65 개 요소로 구성된 배열을 선언하는 것보다 1000 배 빠른 64 개 요소로 여러 배열을 선언합니다.
최근에 64 개 요소를 포함하는 배열을 선언하는 것이 65 개 요소로 동일한 유형의 배열을 선언하는 것보다 훨씬 빠릅니다 (> 1000 배).
이것을 테스트하는 데 사용한 코드는 다음과 같습니다.
public class Tests{
public static void main(String args[]){
double start = System.nanoTime();
int job = 100000000;//100 million
for(int i = 0; i < job; i++){
double[] test = new double[64];
}
double end = System.nanoTime();
System.out.println("Total runtime = " + (end-start)/1000000 + " ms");
}
}
이 작업은 약 6ms에 실행되며 교체 new double[64]
하는 new double[65]
경우 약 7 초가 걸립니다. 이 문제는 작업이 점점 더 많은 스레드에 분산되면 기하 급수적으로 더 심각해집니다.
이 문제는 또한 다음과 같은 배열의 다른 유형의 발생 int[65]
또는 String[65]
. 이 문제는 큰 문자열 String test = "many characters";
에서는 발생하지 않지만 다음으로 변경되면 발생하기 시작합니다.String test = i + "";
왜 이것이 사실인지 그리고이 문제를 피할 수 있는지 궁금합니다.
Java VM의 JIT 컴파일러가 수행 한 최적화 로 인해 발생하는 동작을 관찰하고 있습니다 . 이 동작은 최대 64 개 요소의 스칼라 배열로 트리거 될 수 있으며 64보다 큰 배열에서는 트리거되지 않습니다.
자세히 살펴보기 전에 루프의 본문을 자세히 살펴 보겠습니다.
double[] test = new double[64];
몸은 효과가 없습니다 (관찰 가능한 행동) . 즉,이 명령문의 실행 여부에 관계없이 프로그램 실행 외부에서 아무런 차이가 없습니다. 전체 루프에 대해서도 마찬가지입니다. 따라서 코드 옵티마이 저가 루프를 동일한 기능과 다른 타이밍 동작을 가진 무언가 (또는 아무것도) 로 변환 할 수 있습니다.
벤치 마크의 경우 최소한 다음 두 가지 지침을 준수해야합니다. 그렇게했다면 그 차이는 훨씬 더 작았을 것입니다.
- 벤치 마크를 여러 번 실행하여 JIT 컴파일러 (및 최적화 프로그램)를 워밍업합니다.
- 모든 표현의 결과를 사용하고 벤치 마크 끝에 인쇄하십시오.
이제 자세히 살펴 보겠습니다. 당연히 64 요소보다 크지 않은 스칼라 배열에 대해 트리거되는 최적화가 있습니다. 최적화는 탈출 분석의 일부입니다 . 작은 개체와 작은 배열을 힙에 할당하는 대신 스택에 배치하거나 완전히 최적화하는 것이 좋습니다. 2005 년에 작성된 Brian Goetz의 다음 기사에서 이에 대한 정보를 찾을 수 있습니다.
명령 줄 옵션을 사용하여 최적화를 비활성화 할 수 있습니다 -XX:-DoEscapeAnalysis
. 스칼라 배열의 매직 값 64는 명령 줄에서도 변경할 수 있습니다. 다음과 같이 프로그램을 실행하면 64 개와 65 개 요소가있는 배열간에 차이가 없습니다.
java -XX:EliminateAllocationArraySizeLimit=65 Tests
하지만 이러한 명령 줄 옵션을 사용하지 않는 것이 좋습니다. 나는 그것이 현실적인 응용 프로그램에 큰 차이를 만들지 의심합니다. 필자는 일부 의사 벤치 마크의 결과를 기반으로하지 않고 필요성을 절대적으로 확신 할 경우에만 사용합니다.
개체의 크기에 따라 차이가있을 수있는 방법에는 여러 가지가 있습니다.
nosid가 언급했듯이 JITC는 스택에 작은 "로컬"개체를 할당 할 수 있으며 "작은"배열의 크기 컷오프는 64 요소 일 수 있습니다.
스택에 할당하는 것이 힙에 할당하는 것보다 훨씬 빠르며, 더 나아가서 스택은 가비지 수집이 필요하지 않으므로 GC 오버 헤드가 크게 감소합니다. (이 테스트 케이스의 경우 GC 오버 헤드는 총 실행 시간의 80-90 % 일 가능성이 높습니다.)
또한 값이 스택 할당되면 JITC는 "데드 코드 제거"를 수행하고 그 결과가 new
어디에도 사용되지 않는지 확인하고 손실 될 부작용이 없는지 확인한 후 전체 new
작업을 제거 할 수 있습니다 . 그런 다음 (현재 비어있는) 루프 자체가 있습니다.
JITC가 스택 할당을 수행하지 않더라도 특정 크기보다 작은 객체가 더 큰 객체와 다르게 (예 : 다른 "공간"에서) 힙에 할당되는 것이 전적으로 가능합니다. (일반적으로 이것은 그렇게 극적인 타이밍 차이를 생성하지 않습니다.)
'program tip' 카테고리의 다른 글
생성자 이니셜 라이저에서 멤버 배열 초기화 (0) | 2020.09.04 |
---|---|
Javascript가 포함 된 HTML 파일을 편집하기 위해 emacs를 어떻게 구성합니까? (0) | 2020.09.04 |
C # 멤버 변수 초기화; (0) | 2020.09.04 |
PHP 메모리 프로파일 링 (0) | 2020.09.04 |
클래스 다이어그램을 만들기위한 도구 (0) | 2020.09.04 |