Trên Intel, một giá trị biến động không có đối thủ khá rẻ. Nếu chúng ta xem xét trường hợp đơn giản sau:
public static long l;
public static void run() {
if (l == -1)
System.exit(-1);
if (l == -2)
System.exit(-1);
}
Sử dụng khả năng của Java 7 để in mã lắp ráp, phương thức chạy trông giống như sau:
# {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
Nếu bạn nhìn vào 2 tham chiếu để getstatic, tham chiếu đầu tiên liên quan đến tải từ bộ nhớ, tham chiếu thứ hai bỏ qua tải vì giá trị được sử dụng lại từ (các) thanh ghi mà nó đã được tải vào (dài là 64 bit và trên máy tính xách tay 32 bit của tôi nó sử dụng 2 thanh ghi).
Nếu chúng ta làm cho biến l dễ bay hơi thì lắp ráp kết quả sẽ khác.
# {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
Trong trường hợp này, cả hai tham chiếu getstatic đến biến l đều liên quan đến tải từ bộ nhớ, tức là giá trị không thể được giữ trong một thanh ghi qua nhiều lần đọc biến động. Để đảm bảo rằng có một lần đọc nguyên tử, giá trị được đọc từ bộ nhớ chính vào một thanh ghi MMX movsd 0x6fb7b2f0(%ebp),%xmm0
làm cho hoạt động đọc trở thành một lệnh duy nhất (từ ví dụ trước, chúng ta đã thấy rằng giá trị 64 bit thông thường sẽ yêu cầu hai lần đọc 32 bit trên hệ thống 32 bit).
Vì vậy, chi phí tổng thể của một lần đọc biến động sẽ gần tương đương với tải bộ nhớ và có thể rẻ như truy cập bộ nhớ đệm L1. Tuy nhiên, nếu một lõi khác đang ghi vào biến biến động, dòng bộ đệm sẽ bị vô hiệu yêu cầu bộ nhớ chính hoặc có thể là truy cập bộ đệm L3. Chi phí thực tế sẽ phụ thuộc nhiều vào kiến trúc CPU. Ngay cả giữa Intel và AMD, các giao thức đồng tiền trong bộ nhớ cache cũng khác nhau.