ฉันกำลังตรวจสอบฮอตสปอตประสิทธิภาพในแอปพลิเคชันซึ่งใช้เวลา 50% ใน memmove (3) แอปพลิเคชันจะแทรกจำนวนเต็ม 4 ไบต์นับล้านลงในอาร์เรย์ที่จัดเรียงและใช้ memmove เพื่อเลื่อนข้อมูล "ไปทางขวา" เพื่อให้มีพื้นที่ว่างสำหรับค่าที่แทรก
ความคาดหวังของฉันคือการคัดลอกหน่วยความจำนั้นเร็วมากและฉันรู้สึกประหลาดใจที่ใช้เวลากับ memmove นานมาก แต่แล้วฉันก็มีความคิดว่า memmove ทำงานช้าเพราะมันกำลังเคลื่อนย้ายพื้นที่ที่ทับซ้อนกันซึ่งจะต้องดำเนินการแบบวนซ้ำแทนการคัดลอกหน่วยความจำขนาดใหญ่ ฉันเขียน microbenchmark ขนาดเล็กเพื่อดูว่ามีความแตกต่างด้านประสิทธิภาพระหว่าง memcpy และ memmove หรือไม่โดยคาดหวังว่า memcpy จะชนะมือ
ฉันใช้เกณฑ์มาตรฐานบนสองเครื่อง (core i5, core i7) และเห็นว่า memmove เร็วกว่า memcpy จริง ๆ บน core i7 ที่เก่ากว่าแม้จะเร็วกว่าเกือบสองเท่า! ตอนนี้ผมกำลังหาคำอธิบาย
นี่คือเกณฑ์มาตรฐานของฉัน มันคัดลอก 100 mb ด้วย memcpy จากนั้นย้ายประมาณ 100 mb ด้วย memmove ต้นทางและปลายทางทับซ้อนกัน มีการลอง "ระยะทาง" ต่างๆสำหรับต้นทางและปลายทาง การทดสอบแต่ละครั้งจะทำงาน 10 ครั้งพิมพ์เวลาเฉลี่ย
https://gist.github.com/cruppstahl/78a57cdf937bca3d062c
นี่คือผลลัพธ์บน Core i5 (Linux 3.5.0-54-generic # 81 ~ precision1-Ubuntu SMP x86_64 GNU / Linux, gcc คือ 4.6.3 (Ubuntu / Linaro 4.6.3-1ubuntu5) ตัวเลขในวงเล็บคือ ระยะทาง (ขนาดช่องว่าง) ระหว่างต้นทางและปลายทาง:
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 ถูกนำไปใช้เป็นรหัสแอสเซมเบลอร์ที่ปรับให้เหมาะสมกับ SSE โดยคัดลอกจากด้านหลังไปด้านหน้า ใช้การดึงข้อมูลฮาร์ดแวร์ล่วงหน้าเพื่อโหลดข้อมูลลงในแคชและคัดลอก 128 ไบต์ไปยังรีจิสเตอร์ XMM จากนั้นเก็บไว้ที่ปลายทาง
( memcpy-ssse3-back.S , บรรทัด 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 เร็วกว่าแล้ว memcpy? ฉันคาดว่า memcpy จะคัดลอกหน้าหน่วยความจำซึ่งน่าจะเร็วกว่าการวนซ้ำมาก ในกรณีที่แย่ที่สุดฉันคาดว่า memcpy จะเร็วเท่า memmove
PS: ฉันรู้ว่าฉันไม่สามารถแทนที่ memmove ด้วย memcpy ในรหัสของฉันได้ ฉันรู้ว่าตัวอย่างโค้ดผสม C และ C ++ คำถามนี้เป็นเพียงเพื่อการศึกษาเท่านั้น
อัปเดต 1
ฉันทำการทดสอบรูปแบบต่างๆตามคำตอบที่หลากหลาย
- เมื่อเรียกใช้ memcpy สองครั้งการรันครั้งที่สองจะเร็วกว่าครั้งแรก
- เมื่อ "แตะ" บัฟเฟอร์ปลายทางของ memcpy (
memset(b2, 0, BUFFERSIZE...)
) การเรียกใช้ memcpy ครั้งแรกก็เร็วขึ้นเช่นกัน - memcpy ยังช้ากว่า memmove นิดหน่อย
นี่คือผลลัพธ์:
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
ข้อสรุปของฉัน: จากความคิดเห็นของ @Oliver Charlesworth ระบบปฏิบัติการต้องส่งหน่วยความจำกายภาพทันทีที่เข้าถึงบัฟเฟอร์ปลายทาง memcpy เป็นครั้งแรก (หากมีใครรู้วิธี "พิสูจน์" โปรดเพิ่มคำตอบ! ). นอกจากนี้ตามที่ @Mats Petersson กล่าวว่า memmove เป็นแคชที่เป็นมิตรกว่า memcpy
ขอบคุณสำหรับคำตอบและความคิดเห็นที่ยอดเยี่ยม!