Aşağıdaki kurulumla bazı profilleme yaptım: Test makinesi (AMD Athlon64 x2 3800+) başlatıldı, uzun moda geçirildi (kesintiler devre dışı bırakıldı) ve ilgilenilen talimat bir döngüde yürütüldü, 100 iterasyon açıldı ve 1.000 döngü döngüsü. Döngü gövdesi 16 bayta hizalandı. Zaman, döngüden önce ve sonra bir rdtsc talimatı ile ölçüldü. Ek olarak, herhangi bir talimat içermeyen bir kukla döngü yürütüldü (döngü yinelemesi başına 2 döngü ve geri kalanı için 14 döngü ölçüldü) ve sonuç, komut profili oluşturma süresinin sonucundan çıkarıldı.
Aşağıdaki talimatlar ölçüldü:
- "
lock cmpxchg [rsp - 8], rdx
" (hem karşılaştırmalı hem de uyumsuz),
- "
lock xadd [rsp - 8], rdx
",
- "
lock bts qword ptr [rsp - 8], 1
"
Her durumda ölçülen süre yaklaşık 310 döngüdür, hata yaklaşık +/- 8 döngüdür
Bu, aynı (önbelleğe alınmış) bellekte tekrarlanan yürütme değeridir. Ek bir önbellek kaçırıldığında, süreler önemli ölçüde daha yüksektir. Ayrıca bu, 2 çekirdekten yalnızca biri etkinken yapıldı, bu nedenle önbellek özel olarak sahiplendi ve önbellek senkronizasyonuna gerek yoktu.
Kaçırılan bir önbellekte kilitli talimatın maliyetini değerlendirmek için wbinvld
, kilitli talimatın önüne bir talimat ekledim vewbinvld
artı add [rsp - 8], rax
a'yı karşılaştırma döngüsüne . Her iki durumda da maliyet, komut çifti başına yaklaşık 80.000 döngüdür! Bts kilitlenmesi durumunda, zaman farkı komut başına yaklaşık 180 döngüdür.
Bunun karşılıklı çıktı olduğunu unutmayın, ancak kilitli işlemler serileştirme işlemleri olduğundan, gecikmeden büyük olasılıkla bir fark yoktur.
Sonuç: Kilitli bir işlem ağırdır, ancak bir önbellek kaçırma çok daha ağır olabilir. Ayrıca: kilitli bir işlem önbellek kayıplarına neden olmaz. Yalnızca bir önbellek özelliğine sahip olmadığında önbellek senkronizasyon trafiğine neden olabilir.
Makineyi başlatmak için ReactOS projesinden bir x64 FreeLdr sürümü kullandım. İşte asm kaynak kodu:
#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