Intel에서 경합되지 않는 휘발성 읽기는 매우 저렴합니다. 다음과 같은 간단한 경우를 고려하면 :
public static long l;
public static void run() {
if (l == -1)
System.exit(-1);
if (l == -2)
System.exit(-1);
}
Java 7의 어셈블리 코드 인쇄 기능을 사용하면 run 메소드가 다음과 같이 보입니다.
# {method} 'run2' '()V' in 'Test2'
# [sp+0x10] (sp of caller)
0xb396ce80: mov %eax,-0x3000(%esp)
0xb396ce87: push %ebp
0xb396ce88: sub $0x8,%esp ;*synchronization entry
; - Test2::run2@-1 (line 33)
0xb396ce8e: mov $0xffffffff,%ecx
0xb396ce93: mov $0xffffffff,%ebx
0xb396ce98: mov $0x6fa2b2f0,%esi ; {oop('Test2')}
0xb396ce9d: mov 0x150(%esi),%ebp
0xb396cea3: mov 0x154(%esi),%edi ;*getstatic l
; - Test2::run@0 (line 33)
0xb396cea9: cmp %ecx,%ebp
0xb396ceab: jne 0xb396ceaf
0xb396cead: cmp %ebx,%edi
0xb396ceaf: je 0xb396cece ;*getstatic l
; - Test2::run@14 (line 37)
0xb396ceb1: mov $0xfffffffe,%ecx
0xb396ceb6: mov $0xffffffff,%ebx
0xb396cebb: cmp %ecx,%ebp
0xb396cebd: jne 0xb396cec1
0xb396cebf: cmp %ebx,%edi
0xb396cec1: je 0xb396ceeb ;*return
; - Test2::run@28 (line 40)
0xb396cec3: add $0x8,%esp
0xb396cec6: pop %ebp
0xb396cec7: test %eax,0xb7732000 ; {poll_return}
;... lines removed
getstatic에 대한 2 개의 참조를 살펴보면 첫 번째는 메모리에서로드를 포함하고 두 번째는 이미로드 된 레지스터에서 값이 재사용되므로로드를 건너 뜁니다 (long은 64 비트이고 내 32 비트 랩톱에서는 2 개의 레지스터를 사용합니다).
l 변수를 휘발성으로 만들면 결과 어셈블리가 다릅니다.
# {method} 'run2' '()V' in 'Test2'
# [sp+0x10] (sp of caller)
0xb3ab9340: mov %eax,-0x3000(%esp)
0xb3ab9347: push %ebp
0xb3ab9348: sub $0x8,%esp ;*synchronization entry
; - Test2::run2@-1 (line 32)
0xb3ab934e: mov $0xffffffff,%ecx
0xb3ab9353: mov $0xffffffff,%ebx
0xb3ab9358: mov $0x150,%ebp
0xb3ab935d: movsd 0x6fb7b2f0(%ebp),%xmm0 ; {oop('Test2')}
0xb3ab9365: movd %xmm0,%eax
0xb3ab9369: psrlq $0x20,%xmm0
0xb3ab936e: movd %xmm0,%edx ;*getstatic l
; - Test2::run@0 (line 32)
0xb3ab9372: cmp %ecx,%eax
0xb3ab9374: jne 0xb3ab9378
0xb3ab9376: cmp %ebx,%edx
0xb3ab9378: je 0xb3ab93ac
0xb3ab937a: mov $0xfffffffe,%ecx
0xb3ab937f: mov $0xffffffff,%ebx
0xb3ab9384: movsd 0x6fb7b2f0(%ebp),%xmm0 ; {oop('Test2')}
0xb3ab938c: movd %xmm0,%ebp
0xb3ab9390: psrlq $0x20,%xmm0
0xb3ab9395: movd %xmm0,%edi ;*getstatic l
; - Test2::run@14 (line 36)
0xb3ab9399: cmp %ecx,%ebp
0xb3ab939b: jne 0xb3ab939f
0xb3ab939d: cmp %ebx,%edi
0xb3ab939f: je 0xb3ab93ba ;*return
;... lines removed
이 경우 변수 l에 대한 getstatic 참조는 모두 메모리에서로드를 포함합니다. 즉, 값은 여러 휘발성 읽기에서 레지스터에 보관 될 수 없습니다. 원자 적 읽기가 있는지 확인하기 위해 값을 주 메모리에서 MMX 레지스터 movsd 0x6fb7b2f0(%ebp),%xmm0
로 읽어 읽기 작업을 단일 명령으로 만듭니다 (이전 예제에서 64 비트 값은 일반적으로 32 비트 시스템에서 두 개의 32 비트 읽기가 필요함을 보았습니다).
따라서 휘발성 읽기의 전체 비용은 대략 메모리로드와 동일하며 L1 캐시 액세스만큼 저렴할 수 있습니다. 그러나 다른 코어가 휘발성 변수에 쓰는 경우 캐시 라인은 주 메모리 또는 L3 캐시 액세스를 요구하는 무효화됩니다. 실제 비용은 CPU 아키텍처에 따라 크게 달라집니다. Intel과 AMD 간에도 캐시 일관성 프로토콜이 다릅니다.