Tôi đã thực hiện một số cấu hình với thiết lập sau: Máy thử nghiệm (AMD Athlon64 x2 3800+) đã được khởi động, chuyển sang chế độ dài (ngắt ngắt) và lệnh quan tâm được thực hiện trong một vòng lặp, 100 lần lặp được bỏ cuộn và 1.000 chu kỳ vòng lặp. Nội dung vòng lặp đã được căn chỉnh thành 16 byte. Thời gian được đo bằng lệnh rdtsc trước và sau vòng lặp. Ngoài ra, một vòng lặp giả không có lệnh nào được thực hiện (đo 2 chu kỳ cho mỗi lần lặp vòng lặp và 14 chu kỳ cho phần còn lại) và kết quả được trừ từ kết quả của thời gian lập hồ sơ lệnh.
Các hướng dẫn sau được đo:
- "
lock cmpxchg [rsp - 8], rdx
" (cả đối sánh so sánh và không khớp),
- "
lock xadd [rsp - 8], rdx
",
- "
lock bts qword ptr [rsp - 8], 1
"
Trong tất cả các trường hợp, thời gian đo được là khoảng 310 chu kỳ, sai số là khoảng +/- 8 chu kỳ
Đây là giá trị để thực hiện lặp lại trên cùng một bộ nhớ (được lưu trong bộ nhớ đệm). Với việc bỏ lỡ bộ nhớ cache bổ sung, thời gian sẽ cao hơn đáng kể. Ngoài ra, điều này được thực hiện với chỉ một trong 2 lõi hoạt động, vì vậy bộ nhớ cache được sở hữu độc quyền và không cần đồng bộ hóa bộ nhớ cache.
Để đánh giá chi phí của một lệnh bị khóa khi bỏ lỡ bộ nhớ cache, tôi đã thêm một wbinvld
lệnh trước lệnh bị khóa và đặt dấu wbinvld
cộngadd [rsp - 8], rax
vào vòng lặp so sánh. Trong cả hai trường hợp, chi phí là khoảng 80.000 chu kỳ cho mỗi cặp lệnh! Trong trường hợp khóa bts, chênh lệch thời gian là khoảng 180 chu kỳ cho mỗi lệnh.
Lưu ý rằng đây là thông lượng tương hỗ, nhưng vì các hoạt động bị khóa là các hoạt động tuần tự hóa, có thể không có sự khác biệt đối với độ trễ.
Kết luận: một hoạt động bị khóa là nặng, nhưng bỏ lỡ bộ nhớ cache có thể nặng hơn nhiều. Ngoài ra: một hoạt động bị khóa không gây ra bỏ lỡ bộ nhớ cache. Nó chỉ có thể gây ra lưu lượng đồng bộ hóa bộ nhớ cache, khi một dòng bộ nhớ cache không được sở hữu độc quyền.
Để khởi động máy, tôi đã sử dụng phiên bản x64 của FreeLdr từ dự án ReactOS. Đây là mã nguồn asm:
#define LOOP_COUNT 1000
#define UNROLLED_COUNT 100
PUBLIC ProfileDummy
ProfileDummy:
cli
// Get current TSC value into r8
rdtsc
mov r8, rdx
shl r8, 32
or r8, rax
mov rcx, LOOP_COUNT
jmp looper1
.align 16
looper1:
REPEAT UNROLLED_COUNT
// nothing, or add something to compare against
ENDR
dec rcx
jnz looper1
// Put new TSC minus old TSC into rax
rdtsc
shl rdx, 32
or rax, rdx
sub rax, r8
ret
PUBLIC ProfileFunction
ProfileFunction:
cli
rdtsc
mov r8, rdx
shl r8, 32
or r8, rax
mov rcx, LOOP_COUNT
jmp looper2
.align 16
looper2:
REPEAT UNROLLED_COUNT
// Put here the code you want to profile
// make sure it doesn't mess up non-volatiles or r8
lock bts qword ptr [rsp - 8], 1
ENDR
dec rcx
jnz looper2
rdtsc
shl rdx, 32
or rax, rdx
sub rax, r8
ret