Java와 CPU의 관계

5465 단어 javacpu
사실 자바를 쓰는 사람은 CPU와 아무런 관계가 없는 것 같다. 가장 많은 것은 우리가 앞에서 언급한 CPU를 어떻게 채우는지, 어떻게 스레드를 설정하는지와 관계가 있지만 그 알고리즘은 참고일 뿐이고 많은 장면이 다르기 때문에 실제적인 수단으로 해결해야 한다.그리고 CPU를 가득 채운 후에 우리는 CPU를 그렇게 가득 채우지 않도록 어떻게 해야 할지 고민할 것이다. 허허, 인간, 이렇게 XX, 허허, 됐어. 본고는 다른 것들을 말하고자 한다. 아마도 자바의 코드를 쓸 때 CPU에 관심을 갖지 않아도 될 것이다. 업무를 만족시키는 것이 가장 중요한 일이기 때문이다. 만약에 프레임워크 단계를 하려면 프레임워크에 공유 데이터 캐시 같은 것을 많이 제공해야 한다.중간에 필연적으로 많은 데이터의 징용 문제가 존재한다. 물론 자바는concurrent 패키지의 종류를 많이 제공했다. 당신은 그것을 사용할 수 있다. 그러나 그 내부에서 어떻게 하는지 알아야 디테일을 잘 사용할 수 있다. 그렇지 않으면 차라리 사용하지 않는 것이 낫다. 본고는 이런 내용을 중점적으로 논술하지 않을 것이다. 왜냐하면 제목당: 우리는 CPU를 말해야 하기 때문이다.
아니면 그 말, 자바와 CPU는 별로 상관이 없는 것 같은데, 우리 지금 무슨 상관이 있는지 이야기합시다.
1. 공유 요소를 만났을 때 우리는 보통volatile를 통해 일치성을 확보하는 작업을 한다. 즉, 절대적인 가시성이다. 이른바 가시성이란 이 데이터를 사용할 때마다 CPU는cache의 내용을 사용하지 않고 메모리에서 데이터를 한 번 캡처한다. 그리고 이 과정은 다중 CPU에 여전히 유효하다. 즉, 상당한 CPU와 메모리 사이가 이때 동기화되는 것이다.CPU는 버스처럼 Lockaddl 0과 유사한 어셈블리 명령을 보내지만 +0은 아무것도 하지 않습니다.그러나 이 명령이 완성되면 후속 작업은 이 요소의 다른 스레드 접근, 즉 그가 실현할 수 있는 절대적인 가시성에 영향을 주지 않지만 일치성 조작을 실현할 수 없다. 즉, volatile가 실현할 수 없는 것은 i++와 같은 조작의 일치성(다중 스레드에서 병발)이다. 왜냐하면 i++ 조작은 다음과 같이 분해되기 때문이다.

int tmp = i;
tmp = tmp + 1;
i = tmp;
이 세 가지 절차를 완성하면 i++가 왜 다른 일을 먼저 하고 스스로 1을 추가할 수 있는지 알 수 있다. 왜냐하면 값이 다른 변수에 부여되었기 때문이다.
2. 우리가 다중 스레드의 일치성을 사용하려면 자물쇠의 메커니즘을 사용해야 한다. 현재 Atomic*와 같은 것들은 기본적으로 이런 요구를 만족시킬 수 있다. 내부에 많은 unsafe 유형의 방법을 제공했고 절대적인 가시성을 가진 데이터를 끊임없이 비교함으로써 얻은 데이터가 최신이라는 것을 보장한다.이어서 우리는 계속해서 CPU의 다른 일을 이야기했다.
3. 이전에 우리는 CPU를 가득 채우기 위해 뛰었지만 어쨌든 달리기가 불만이다. 왜냐하면 우리는 메모리와 CPU의 지연을 소홀히 한다고 말했기 때문이다. 오늘 여기까지 언급한 이상 우리는 간단하게 지연을 말한다. 일반적으로 현재의 CPU는 3급cache가 있는데 연대에 따라 지연이 다르기 때문에 구체적인 숫자는 대략적인 것만 말할 수 있다. 현재의 CPU는 보통 1급cache의 지연은 1-2ns이고 2급cache는 보통 몇 ns에서 10ns 정도이다.3급cache는 일반적으로 30ns에서 50ns까지 다양하고 메모리 접근은 보편적으로 70ns, 심지어 더 많아진다(컴퓨터의 발전 속도가 매우 빠르고 이 값도 일부 CPU의 데이터에 불과하기 때문에 범위 참조를 할 뿐이다).이 지연은 매우 작지만 모두 납초 단계이다. 당신은 당신의 프로그램이 지령 연산으로 분리될 때 많은 CPU 상호작용이 있음을 발견할 수 있다. 매번 상호작용의 지연이 이렇게 큰 편차가 있으면 이때 시스템 성능은 변화가 있을 것이다.
4. 방금 말한 volatile로 돌아가면 메모리에서 데이터를 얻을 때마다cache를 포기하는 것이다. 자연히 어떤 단일 라인의 작업에서 더욱 느려진다. 어떤 때는 우리도 이렇게 해야 한다. 심지어 읽기와 쓰기 작업도 일치성을 요구한다. 심지어 전체 데이터 블록이 동기화되기 때문에 우리는 어느 정도에 자물쇠의 입도를 낮출 수 있지만 자물쇠가 완전히 없으면 안 된다.CPU 자체의 레벨이라도 명령 레벨의 제한이 있습니다.
5. CPU 자체 단계의 원자 조작을 일반적으로 장벽이라고 하는데 읽기 장벽, 쓰기 장벽 등이 있다. 일반적으로 하나의 점에 기초한 촉발이다. 프로그램이 여러 개의 지령을 CPU에 보낼 때 어떤 지령은 반드시 프로그램의 순서에 따라 집행하는 것이 아니라 어떤 지령은 반드시 프로그램의 순서에 따라 집행해야 한다. 최종적으로 일치를 보장하기만 하면 된다.정렬에서 JIT는 실행할 때 바뀌고 CPU 명령 수준도 바뀐다. 왜냐하면 실행할 때 명령을 최적화하여 프로그램을 더욱 빨리 달리게 하기 때문이다.
6. CPU 레벨은 메모리에 대해cache line의 조작을 한다. 소위cache line은 하나의 메모리를 연속적으로 읽는다. 일반적으로 CPU 모델과 구조와 관계가 있다. 현재 많은 CPU는 연속적인 메모리를 읽을 때마다 64byte이고 초기에는 32byte가 있었기 때문에 일부 수조가 훑어볼 때 비교적 빠르다(열을 바탕으로 훑어보는 것은 매우 느리다). 그러나 이것은 완전히 옳지 않다. 다음은 상반된 상황에 비추어 볼 것이다.
7. CPU가 데이터에 대한 수정이 발생하면 CPU가 데이터에 대한 수정 상태를 말해야 한다. 데이터가 모두 읽히면 다중 CPU 아래에서 다중 스레드를 병렬적으로 읽을 수 있고 데이터 블록에 대한 쓰기 작업이 발생할 때 달라진다. 데이터 블록은 독점, 수정, 실효 상태가 있고 데이터가 수정되면 자연히 실효된다. 다중 CPU 아래에서 여러 스레드가 같은 데이터 블록을 수정할 때CPU 간의 버스 데이터 복사(QPI)가 발생할 수 있습니다. 물론 같은 데이터로 수정할 때 우리는 어쩔 수 없지만 6시의cache line으로 돌아가면 문제가 비교적 번거롭습니다. 만약에 데이터가 같은 수조에 있고 수조에 있는 원소가 한 CPU에 동시cache line에 들어갈 때 다중 라인의 QPI는 매우 빈번합니다.어떤 때는 수조에 조립된 것이 대상이라도 이 문제가 발생한다. 예를 들어

class InputInteger {
private int value;
public InputInteger(int i) {
this.value = i;
}
}
InputInteger[] integers = new InputInteger[SIZE];
for(int i=0 ; i < SIZE ; i++) {
integers[i] = new InputInteger(i);
}
이때 당신은integers에 놓인 모든 것이 대상임을 알 수 있습니다. 수조에 대상의 인용만 있지만 대상의 배열은 이론적으로 각자의 대상이 독립적이고 연속적으로 저장되지 않는다고 합니다. 그러나 자바는 대상의 메모리를 분배할 때 에덴 구역에서 연속적으로 분배됩니다. for순환할 때 다른 라인의 접속이 없으면 이 대상들은 함께 저장됩니다.GC에 의해 OLD 구역에 놓여도 함께 놓을 가능성이 높기 때문에 간단한 대상으로cache라인을 해결한 후에 전체 수조를 수정하는 방식은 믿을 수 없는 것 같다. int가 4바이트이기 때문에 64모드에서 이 크기는 24바이트(4byte가 보충되어 있음), 바늘 압축 오픈은 16byte이다.즉, 매번 cpu는 3-4개의 대상을 정렬할 수 있다. 어떻게 CPUcache를 만들었는지 알 수 있지만 시스템의 QPI에 영향을 주지 않는다. 대상을 분리해서 완성하려고 하지 마라. GC 프로세스 메모리 복사 과정은 함께 복사할 수 있기 때문이다. 가장 좋은 방법은 보충하는 것이다. 메모리를 약간 낭비하지만 이것이 가장 믿을 만한 방법이다. 바로 대상을 64바이트로 정렬하는 것이다. 상기 바늘을 열지 않으면 24byte가 압축되고 이때 40바이트가 더 있다.대상 내부에 5개의 롱만 추가하면 된다.

class InputInteger {
public int value;
private long a1,a2,a3,a4,a5;
}
허허, 이 방법은 매우 촌스럽지만 매우 유용하다. 어떤 때는 Jvm를 번역할 때 이 몇 개의 매개 변수가 아무것도 하지 않은 것을 발견하고 바로 너에게 해 주었다. 최적화는 효과가 없다. 흙 방법에 흙을 넣는 방법은 한 방법체에서 이 다섯 개의 매개 변수에 대해 간단하게 조작하는 것이다. 그러나 이 방법은 영원히 그것을 사용하지 않으면 된다.
8. CPU라는 단계에서 가급적 먼저 할 수 있는 도리가 왕인 것은 아니다. 자물쇠를 얻는 것과 같은 조작은 Atomic Integer Field Updater의 조작이다. 만약에 get AndSet(true)을 한 라인에서 호출하면 달리기가 매우 빠르다는 것을 발견할 수 있다. 다핵 CPU 아래에서 시작이 느려지는 것을 발견할 수 있다. 왜 위에서 분명히 말했는가. get AndSet 안은 수정한 후에 비교하기 때문에 먼저 고치고 QPI가 높을 것이다. 그래서 이때먼저 get 조작을 하고 수정하는 것이 비교적 좋은 방법이다.그리고 한 번 얻는 것이다. 얻지 못하면 양보하고 다른 라인에서 다른 일을 하도록 한다.
9. CPU는 어떤 CPU가 바쁘고 바쁘지 않은 문제를 해결하기 위해 많은 알고리즘이 해결된다. 예를 들어 NUMA는 그 중의 한 방안이지만 어떤 구조든 일정한 장면에서 비교적 유용하고 모든 장면에 효과가 있는 것은 아니다.대기열 잠금 메커니즘이 있어 CPU 상태 관리를 완성하지만 이것은 캐치 라인의 문제가 존재한다. 상태가 자주 바뀌기 때문에 각종 응용 프로그램의 핵은 CPU에 맞추기 위해 알고리즘을 내서 CPU를 더욱 효과적으로 활용할 수 있다. 예를 들어 CLH 대열 등이다.
이 방면에 관한 세부 사항은 일반 변수 순환 중첩과volatile 유형의 작업, 그리고 Atomic* 시리즈로 하는 것과 완전히 다르다.다차원 수조 순환은 서로 다른 위도에 따라 뒤로 순서대로 순환하는 것도 다르고 세부적인 부분이 많아서 왜 실제 최적화 과정에서 영감을 얻었는지 알 수 있다.자물쇠의 디테일은 너무 가늘고 어지럽다. 시스템 밑바닥의 단계에서 항상 경량급의 원자 조작이 있다. 누가 그의 코드를 자물쇠를 넣을 필요가 없다고 하든지 간에 가장 가느다란 것은 CPU가 매 순간 하나의 명령만 실행할 수 있을 정도로 간단하다. 다중 핵심 CPU는 버스 단계에서도 공유 구역이 있어 일부 내용을 제어할 수 있다. 읽기 등급, 쓰기 등급, 메모리 등급 등이 있고 서로 다른 장면에서 자물쇠의 입도를 최대한 낮출 수 있다.그렇다면 시스템의 성능은 말하지 않아도 정상적인 결과이다.

좋은 웹페이지 즐겨찾기