@@ -577,7 +577,7 @@ ACQUIRE 는 해당 오퍼레이션의 로드 부분에만 적용되고 RELEASE
577
577
데이터 의존성 배리어 (역사적)
578
578
-----------------------------
579
579
580
- 리눅스 커널 v4.15 기준으로, smp_read_barrier_depends () 가 READ_ONCE() 에
580
+ 리눅스 커널 v4.15 기준으로, smp_mb () 가 DEC Alpha 용 READ_ONCE() 코드에
581
581
추가되었는데, 이는 이 섹션에 주의를 기울여야 하는 사람들은 DEC Alpha 아키텍쳐
582
582
전용 코드를 만드는 사람들과 READ_ONCE() 자체를 만드는 사람들 뿐임을 의미합니다.
583
583
그런 분들을 위해, 그리고 역사에 관심 있는 분들을 위해, 여기 데이터 의존성
@@ -2664,144 +2664,6 @@ CPU 코어는 프로그램의 인과성이 유지된다고만 여겨진다면
2664
2664
수도 있습니다.
2665
2665
2666
2666
2667
- 캐시 일관성
2668
- -----------
2669
-
2670
- 하지만 삶은 앞에서 이야기한 것처럼 단순하지 않습니다: 캐시들은 일관적일 것으로
2671
- 기대되지만, 그 일관성이 순서에도 적용될 거라는 보장은 없습니다. 한 CPU 에서
2672
- 만들어진 변경 사항은 최종적으로는 시스템의 모든 CPU 에게 보여지게 되지만, 다른
2673
- CPU 들에게도 같은 순서로 보이게 될 거라는 보장은 없다는 뜻입니다.
2674
-
2675
-
2676
- 두개의 CPU (1 & 2) 가 달려 있고, 각 CPU 에 두개의 데이터 캐시(CPU 1 은 A/B 를,
2677
- CPU 2 는 C/D 를 갖습니다)가 병렬로 연결되어 있는 시스템을 다룬다고 생각해
2678
- 봅시다:
2679
-
2680
- :
2681
- : +--------+
2682
- : +---------+ | |
2683
- +--------+ : +--->| Cache A |<------->| |
2684
- | | : | +---------+ | |
2685
- | CPU 1 |<---+ | |
2686
- | | : | +---------+ | |
2687
- +--------+ : +--->| Cache B |<------->| |
2688
- : +---------+ | |
2689
- : | Memory |
2690
- : +---------+ | System |
2691
- +--------+ : +--->| Cache C |<------->| |
2692
- | | : | +---------+ | |
2693
- | CPU 2 |<---+ | |
2694
- | | : | +---------+ | |
2695
- +--------+ : +--->| Cache D |<------->| |
2696
- : +---------+ | |
2697
- : +--------+
2698
- :
2699
-
2700
- 이 시스템이 다음과 같은 특성을 갖는다 생각해 봅시다:
2701
-
2702
- (*) 홀수번 캐시라인은 캐시 A, 캐시 C 또는 메모리에 위치할 수 있음;
2703
-
2704
- (*) 짝수번 캐시라인은 캐시 B, 캐시 D 또는 메모리에 위치할 수 있음;
2705
-
2706
- (*) CPU 코어가 한개의 캐시에 접근하는 동안, 다른 캐시는 - 더티 캐시라인을
2707
- 메모리에 내리거나 추측성 로드를 하거나 하기 위해 - 시스템의 다른 부분에
2708
- 액세스 하기 위해 버스를 사용할 수 있음;
2709
-
2710
- (*) 각 캐시는 시스템의 나머지 부분들과 일관성을 맞추기 위해 해당 캐시에
2711
- 적용되어야 할 오퍼레이션들의 큐를 가짐;
2712
-
2713
- (*) 이 일관성 큐는 캐시에 이미 존재하는 라인에 가해지는 평범한 로드에 의해서는
2714
- 비워지지 않는데, 큐의 오퍼레이션들이 이 로드의 결과에 영향을 끼칠 수 있다
2715
- 할지라도 그러함.
2716
-
2717
- 이제, 첫번째 CPU 에서 두개의 쓰기 오퍼레이션을 만드는데, 해당 CPU 의 캐시에
2718
- 요청된 순서로 오퍼레이션이 도달됨을 보장하기 위해 두 오퍼레이션 사이에 쓰기
2719
- 배리어를 사용하는 상황을 상상해 봅시다:
2720
-
2721
- CPU 1 CPU 2 COMMENT
2722
- =============== =============== =======================================
2723
- u == 0, v == 1 and p == &u, q == &u
2724
- v = 2;
2725
- smp_wmb(); v 의 변경이 p 의 변경 전에 보일 것을
2726
- 분명히 함
2727
- <A:modify v=2> v 는 이제 캐시 A 에 독점적으로 존재함
2728
- p = &v;
2729
- <B:modify p=&v> p 는 이제 캐시 B 에 독점적으로 존재함
2730
-
2731
- 여기서의 쓰기 메모리 배리어는 CPU 1 의 캐시가 올바른 순서로 업데이트 된 것으로
2732
- 시스템의 다른 CPU 들이 인지하게 만듭니다. 하지만, 이제 두번째 CPU 가 그 값들을
2733
- 읽으려 하는 상황을 생각해 봅시다:
2734
-
2735
- CPU 1 CPU 2 COMMENT
2736
- =============== =============== =======================================
2737
- ...
2738
- q = p;
2739
- x = *q;
2740
-
2741
- 위의 두개의 읽기 오퍼레이션은 예상된 순서로 일어나지 못할 수 있는데, 두번째 CPU
2742
- 의 한 캐시에 다른 캐시 이벤트가 발생해 v 를 담고 있는 캐시라인의 해당 캐시에의
2743
- 업데이트가 지연되는 사이, p 를 담고 있는 캐시라인은 두번째 CPU 의 다른 캐시에
2744
- 업데이트 되어버렸을 수 있기 때문입니다.
2745
-
2746
- CPU 1 CPU 2 COMMENT
2747
- =============== =============== =======================================
2748
- u == 0, v == 1 and p == &u, q == &u
2749
- v = 2;
2750
- smp_wmb();
2751
- <A:modify v=2> <C:busy>
2752
- <C:queue v=2>
2753
- p = &v; q = p;
2754
- <D:request p>
2755
- <B:modify p=&v> <D:commit p=&v>
2756
- <D:read p>
2757
- x = *q;
2758
- <C:read *q> 캐시에 업데이트 되기 전의 v 를 읽음
2759
- <C:unbusy>
2760
- <C:commit v=2>
2761
-
2762
- 기본적으로, 두개의 캐시라인 모두 CPU 2 에 최종적으로는 업데이트 될 것이지만,
2763
- 별도의 개입 없이는, 업데이트의 순서가 CPU 1 에서 만들어진 순서와 동일할
2764
- 것이라는 보장이 없습니다.
2765
-
2766
-
2767
- 여기에 개입하기 위해선, 데이터 의존성 배리어나 읽기 배리어를 로드 오퍼레이션들
2768
- 사이에 넣어야 합니다 (v4.15 부터는 READ_ONCE() 매크로에 의해 무조건적으로
2769
- 그렇게 됩니다). 이렇게 함으로써 캐시가 다음 요청을 처리하기 전에 일관성 큐를
2770
- 처리하도록 강제하게 됩니다.
2771
-
2772
- CPU 1 CPU 2 COMMENT
2773
- =============== =============== =======================================
2774
- u == 0, v == 1 and p == &u, q == &u
2775
- v = 2;
2776
- smp_wmb();
2777
- <A:modify v=2> <C:busy>
2778
- <C:queue v=2>
2779
- p = &v; q = p;
2780
- <D:request p>
2781
- <B:modify p=&v> <D:commit p=&v>
2782
- <D:read p>
2783
- smp_read_barrier_depends()
2784
- <C:unbusy>
2785
- <C:commit v=2>
2786
- x = *q;
2787
- <C:read *q> 캐시에 업데이트 된 v 를 읽음
2788
-
2789
-
2790
- 이런 부류의 문제는 DEC Alpha 계열 프로세서들에서 발견될 수 있는데, 이들은
2791
- 데이터 버스를 좀 더 잘 사용해 성능을 개선할 수 있는, 분할된 캐시를 가지고 있기
2792
- 때문입니다. 대부분의 CPU 는 하나의 읽기 오퍼레이션의 메모리 액세스가 다른 읽기
2793
- 오퍼레이션에 의존적이라면 데이터 의존성 배리어를 내포시킵니다만, 모두가 그런건
2794
- 아니기 때문에 이점에 의존해선 안됩니다.
2795
-
2796
- 다른 CPU 들도 분할된 캐시를 가지고 있을 수 있지만, 그런 CPU 들은 평범한 메모리
2797
- 액세스를 위해서도 이 분할된 캐시들 사이의 조정을 해야만 합니다. Alpha 는 가장
2798
- 약한 메모리 순서 시맨틱 (semantic) 을 선택함으로써 메모리 배리어가 명시적으로
2799
- 사용되지 않았을 때에는 그런 조정이 필요하지 않게 했으며, 이는 Alpha 가 당시에
2800
- 더 높은 CPU 클락 속도를 가질 수 있게 했습니다. 하지만, (다시 말하건대, v4.15
2801
- 이후부터는) Alpha 아키텍쳐 전용 코드와 READ_ONCE() 매크로 내부에서를 제외하고는
2802
- smp_read_barrier_depends() 가 사용되지 않아야 함을 알아두시기 바랍니다.
2803
-
2804
-
2805
2667
캐시 일관성 VS DMA
2806
2668
------------------
2807
2669
@@ -2962,10 +2824,8 @@ Alpha CPU 의 일부 버전은 분할된 데이터 캐시를 가지고 있어서
2962
2824
데이터의 발견을 올바른 순서로 일어나게 하기 때문입니다.
2963
2825
2964
2826
리눅스 커널의 메모리 배리어 모델은 Alpha 에 기초해서 정의되었습니다만, v4.15
2965
- 부터는 리눅스 커널이 READ_ONCE() 내에 smp_read_barrier_depends() 를 추가해서
2966
- Alpha 의 메모리 모델로의 영향력이 크게 줄어들긴 했습니다.
2967
-
2968
- 위의 "캐시 일관성" 서브섹션을 참고하세요.
2827
+ 부터는 Alpha 용 READ_ONCE() 코드 내에 smp_mb() 가 추가되어서 메모리 모델로의
2828
+ Alpha 의 영향력이 크게 줄어들었습니다.
2969
2829
2970
2830
2971
2831
가상 머신 게스트
0 commit comments