Zamanının% 50'sini memmove'da geçiren bir uygulamada performans sıcak noktalarını araştırıyorum (3). Uygulama, sıralanmış dizilere milyonlarca 4 baytlık tamsayı ekler ve eklenen değere yer açmak için verileri "sağa" kaydırmak için memmove kullanır.
Beklentim, hafıza kopyalamanın son derece hızlı olmasıydı ve memmove'da bu kadar çok zaman harcanmasına şaşırdım. Ama sonra memmove'nin yavaş olduğu fikrine kapıldım çünkü büyük bellek sayfalarını kopyalamak yerine, sıkı bir döngü içinde uygulanması gereken üst üste binen bölgeleri hareket ettiriyor. Memcpy ile memmove arasında bir performans farkı olup olmadığını öğrenmek için küçük bir mikro ölçüt yazdım ve memcpy'nin ellerini aşağı çekmesini bekledim.
Karşılaştırmamı iki makinede (çekirdek i5, çekirdek i7) çalıştırdım ve memmove'un aslında memcpy'den daha hızlı olduğunu gördüm, eski i7'de neredeyse iki kat daha hızlı! Şimdi açıklamalar arıyorum.
İşte kıyaslamam. Memcpy ile 100 mb kopyalar ve ardından memmove ile yaklaşık 100 mb hareket eder; kaynak ve hedef çakışıyor. Kaynak ve hedef için çeşitli "mesafeler" denenir. Her test 10 kez çalıştırılır, ortalama süre yazdırılır.
https://gist.github.com/cruppstahl/78a57cdf937bca3d062c
İşte Core i5 (Linux 3.5.0-54-generic # 81 ~ exact1-Ubuntu SMP x86_64 GNU / Linux, gcc 4.6.3'tür (Ubuntu / Linaro 4.6.3-1ubuntu5). Parantez içindeki sayı kaynak ve hedef arasındaki mesafe (boşluk boyutu):
memcpy 0.0140074
memmove (002) 0.0106168
memmove (004) 0.01065
memmove (008) 0.0107917
memmove (016) 0.0107319
memmove (032) 0.0106724
memmove (064) 0.0106821
memmove (128) 0.0110633
Memmove, arkadan öne doğru kopyalayan, SSE optimize edilmiş bir montajcı kodu olarak uygulanır. Verileri önbelleğe yüklemek için donanım ön belleğini kullanır ve 128 baytı XMM kayıtlarına kopyalar, ardından bunları hedefte depolar.
( memcpy-ssse3-back.S , satırlar 1650 ff)
L(gobble_ll_loop):
prefetchnta -0x1c0(%rsi)
prefetchnta -0x280(%rsi)
prefetchnta -0x1c0(%rdi)
prefetchnta -0x280(%rdi)
sub $0x80, %rdx
movdqu -0x10(%rsi), %xmm1
movdqu -0x20(%rsi), %xmm2
movdqu -0x30(%rsi), %xmm3
movdqu -0x40(%rsi), %xmm4
movdqu -0x50(%rsi), %xmm5
movdqu -0x60(%rsi), %xmm6
movdqu -0x70(%rsi), %xmm7
movdqu -0x80(%rsi), %xmm8
movdqa %xmm1, -0x10(%rdi)
movdqa %xmm2, -0x20(%rdi)
movdqa %xmm3, -0x30(%rdi)
movdqa %xmm4, -0x40(%rdi)
movdqa %xmm5, -0x50(%rdi)
movdqa %xmm6, -0x60(%rdi)
movdqa %xmm7, -0x70(%rdi)
movdqa %xmm8, -0x80(%rdi)
lea -0x80(%rsi), %rsi
lea -0x80(%rdi), %rdi
jae L(gobble_ll_loop)
Memmove neden memcpy'den daha hızlı? Memcpy'nin bellek sayfalarını kopyalamasını beklerdim, bu döngüden çok daha hızlı olmalı. En kötü durumda, memcpy'nin memmove kadar hızlı olmasını beklerdim.
Not: Kodumda memmove'u memcpy ile değiştiremeyeceğimi biliyorum. Kod örneğinin C ve C ++ 'yı karıştırdığını biliyorum. Bu soru gerçekten sadece akademik amaçlar içindir.
GÜNCELLEME 1
Çeşitli cevaplara dayanarak testlerin bazı varyasyonlarını çalıştırdım.
- Memcpy'yi iki kez çalıştırırken, ikinci çalıştırma ilkinden daha hızlıdır.
- Memcpy (
memset(b2, 0, BUFFERSIZE...)
) 'nin hedef tamponuna "dokunduğunuzda", memcpy'nin ilk çalıştırması da daha hızlıdır. - memcpy hala memmove'dan biraz daha yavaştır.
Sonuçlar burada:
memcpy 0.0118526
memcpy 0.0119105
memmove (002) 0.0108151
memmove (004) 0.0107122
memmove (008) 0.0107262
memmove (016) 0.0108555
memmove (032) 0.0107171
memmove (064) 0.0106437
memmove (128) 0.0106648
Sonucum: @Oliver Charlesworth'un bir yorumuna dayanarak, işletim sistemi, memcpy hedef arabelleğine ilk kez erişilir erişilmez fiziksel belleği işlemelidir (birisi bunu nasıl "kanıtlayacağını" biliyorsa, lütfen bir yanıt ekleyin! ). Ek olarak, @Mats Petersson'ın dediği gibi, memmove, memcpy'den daha kolay önbellek dostudur.
Tüm harika cevaplar ve yorumlar için teşekkürler!