Saya akan menentang kebijaksanaan umum di sini yang std::copy
akan memiliki sedikit kehilangan kinerja, hampir tak terlihat. Saya hanya melakukan tes dan menemukan bahwa itu tidak benar: Saya memang melihat perbedaan kinerja. Namun, pemenangnya adalah std::copy
.
Saya menulis implementasi C ++ SHA-2. Dalam pengujian saya, saya hash 5 string menggunakan keempat versi SHA-2 (224, 256, 384, 512), dan saya loop 300 kali. Saya mengukur waktu menggunakan Boost.timer. 300 loop counter itu cukup untuk sepenuhnya menstabilkan hasil saya. Saya menjalankan tes masing-masing 5 kali, bergantian antara memcpy
versi dan std::copy
versi. Kode saya mengambil keuntungan dari mengambil data dalam potongan sebanyak mungkin (banyak implementasi lain beroperasi dengan char
/ char *
, sedangkan saya beroperasi dengan T
/ T *
(di mana T
adalah tipe terbesar dalam implementasi pengguna yang memiliki perilaku overflow yang benar), sehingga akses memori cepat pada tipe terbesar yang saya dapat adalah pusat kinerja algoritma saya. Ini adalah hasil saya:
Waktu (dalam detik) untuk menyelesaikan uji SHA-2
std::copy memcpy % increase
6.11 6.29 2.86%
6.09 6.28 3.03%
6.10 6.29 3.02%
6.08 6.27 3.03%
6.08 6.27 3.03%
Total peningkatan rata-rata kecepatan std :: copy over memcpy: 2.99%
Kompiler saya adalah gcc 4.6.3 pada Fedora 16 x86_64. Bendera pengoptimalan saya adalah -Ofast -march=native -funsafe-loop-optimizations
.
Kode untuk implementasi SHA-2 saya.
Saya memutuskan untuk menjalankan tes pada implementasi MD5 saya juga. Hasilnya jauh lebih tidak stabil, jadi saya memutuskan untuk melakukan 10 kali. Namun, setelah beberapa upaya pertama saya, saya mendapatkan hasil yang sangat bervariasi dari satu lari ke yang berikutnya, jadi saya menduga ada semacam aktivitas OS yang terjadi. Saya memutuskan untuk memulai dari awal.
Pengaturan dan bendera kompiler yang sama. Hanya ada satu versi MD5, dan ini lebih cepat dari SHA-2, jadi saya melakukan 3000 loop pada set yang sama dari 5 string tes.
Ini adalah 10 hasil akhir saya:
Waktu (dalam detik) untuk menyelesaikan tes MD5
std::copy memcpy % difference
5.52 5.56 +0.72%
5.56 5.55 -0.18%
5.57 5.53 -0.72%
5.57 5.52 -0.91%
5.56 5.57 +0.18%
5.56 5.57 +0.18%
5.56 5.53 -0.54%
5.53 5.57 +0.72%
5.59 5.57 -0.36%
5.57 5.56 -0.18%
Total penurunan rata-rata kecepatan std :: copy over memcpy: 0.11%
Kode untuk implementasi MD5 saya
Hasil ini menunjukkan bahwa ada beberapa optimasi yang std :: copy digunakan dalam tes SHA-2 saya yang std::copy
tidak dapat digunakan dalam tes MD5 saya. Dalam tes SHA-2, kedua array diciptakan dalam fungsi yang sama yang disebut std::copy
/ memcpy
. Dalam tes MD5 saya, salah satu array diteruskan ke fungsi sebagai parameter fungsi.
Saya melakukan sedikit pengujian lagi untuk melihat apa yang bisa saya lakukan untuk membuat std::copy
lebih cepat lagi. Jawabannya ternyata sederhana: aktifkan optimasi waktu tautan. Ini adalah hasil saya dengan LTO dihidupkan (opsi -flto di gcc):
Waktu (dalam detik) untuk menyelesaikan uji MD5 dengan -flto
std::copy memcpy % difference
5.54 5.57 +0.54%
5.50 5.53 +0.54%
5.54 5.58 +0.72%
5.50 5.57 +1.26%
5.54 5.58 +0.72%
5.54 5.57 +0.54%
5.54 5.56 +0.36%
5.54 5.58 +0.72%
5.51 5.58 +1.25%
5.54 5.57 +0.54%
Total peningkatan rata-rata kecepatan std :: copy over memcpy: 0.72%
Singkatnya, tampaknya tidak ada penalti kinerja untuk menggunakan std::copy
. Bahkan, tampaknya ada peningkatan kinerja.
Penjelasan hasil
Jadi mengapa mungkin std::copy
memberikan peningkatan kinerja?
Pertama, saya tidak berharap itu lebih lambat untuk implementasi apa pun, selama optimasi inlining dihidupkan. Semua penyusun inline secara agresif; itu mungkin optimasi yang paling penting karena memungkinkan banyak optimasi lainnya. std::copy
dapat (dan saya menduga semua implementasi dunia nyata lakukan) mendeteksi bahwa argumen sepele disalin dan bahwa memori diletakkan secara berurutan. Ini berarti bahwa dalam kasus terburuk, ketika memcpy
legal, std::copy
harus melakukan tidak lebih buruk. Implementasi sepele dari std::copy
yang memcpy
harus memenuhi kriteria kompiler Anda "selalu sebaris ini ketika mengoptimalkan untuk kecepatan atau ukuran".
Namun, std::copy
juga menyimpan lebih banyak informasinya. Saat Anda menelepon std::copy
, fungsi tersebut menjaga jenisnya tetap utuh. memcpy
beroperasi pada void *
, yang membuang hampir semua informasi yang berguna. Sebagai contoh, jika saya memasukkan array std::uint64_t
, kompiler atau pelaksana perpustakaan mungkin dapat memanfaatkan keselarasan 64-bit dengan std::copy
, tetapi mungkin lebih sulit untuk melakukannya dengan memcpy
. Banyak implementasi algoritma seperti ini bekerja dengan terlebih dahulu mengerjakan bagian yang tidak selaras di awal rentang, kemudian bagian yang disejajarkan, kemudian bagian yang tidak selaras di bagian akhir. Jika semuanya dijamin akan disejajarkan, maka kode menjadi lebih sederhana dan lebih cepat, dan lebih mudah bagi prediktor cabang dalam prosesor Anda untuk mendapatkan yang benar.
Optimalisasi prematur?
std::copy
berada dalam posisi yang menarik. Saya berharap itu tidak pernah lebih lambat dari memcpy
dan kadang-kadang lebih cepat dengan kompiler optimisasi modern. Apalagi apa pun yang Anda bisa memcpy
, Anda bisa std::copy
. memcpy
tidak memungkinkan tumpang tindih dalam buffer, sedangkan std::copy
dukungan tumpang tindih dalam satu arah (dengan std::copy_backward
untuk arah lain tumpang tindih). memcpy
hanya bekerja pada pointer, std::copy
bekerja pada setiap iterator ( std::map
, std::vector
, std::deque
, atau sendiri jenis kustom saya). Dengan kata lain, Anda hanya perlu menggunakan std::copy
saat Anda perlu menyalin potongan data di sekitar.
char
dapat ditandatangani atau tidak ditandatangani, tergantung pada implementasinya. Jika jumlah byte bisa> = 128, maka gunakanunsigned char
untuk array byte Anda. (Para(int *)
pemain juga akan lebih aman(unsigned int *)
.)