Dengan asumsi bahwa kita memiliki T myarray[100]
dengan T = int, unsigned int, long long int atau unsigned long long int, apa cara tercepat untuk mengatur ulang semua kontennya menjadi nol (tidak hanya untuk inisialisasi tetapi untuk mengatur ulang konten beberapa kali dalam program saya) ? Mungkin dengan memset?
Pertanyaan yang sama untuk array dinamis seperti T *myarray = new T[100]
.
new
adalah C ++ ...memset
ketika C ++ entah bagaimana terlibat ... :)for
loop sederhana . Namun, yang mengejutkan, Anda dapat melakukan jauh lebih buruk dengan mencoba menjadi pintar.Jawaban:
memset
(dari<string.h>
) mungkin adalah cara standar tercepat, karena biasanya ini adalah rutinitas yang ditulis langsung dalam rakitan dan dioptimalkan dengan tangan.Ngomong-ngomong, di C ++ cara idiomatiknya adalah menggunakan
std::fill
(dari<algorithm>
):yang dapat dioptimalkan secara otomatis menjadi
memset
; Aku cukup yakin bahwa itu akan bekerja secepatmemset
untukint
s, sementara itu mungkin melakukan sedikit lebih buruk untuk jenis yang lebih kecil jika optimizer tidak cukup pintar. Namun, jika ragu, profil.sumber
memset
akan menetapkan integer ke 0; tidak ada pernyataan khusus bahwa semua-bit-nol adalah representasi dari0
. Sebuah Korrigendum Teknis menambahkan jaminan seperti itu, yang termasuk dalam standar ISO C 2011. Saya percaya bahwa all-bits-zero adalah representasi yang valid0
untuk semua tipe integer di semua implementasi C dan C ++ yang ada, itulah sebabnya panitia dapat menambahkan persyaratan itu. (Tidak ada jaminan serupa untuk tipe floating-point atau pointer.)0
. (Dengan bit padding, kemungkinan ada bahwa semua-bit-nol bisa menjadi representasi perangkap). Tetapi bagaimanapun juga, TC seharusnya mengakui dan mengganti teks yang rusak, jadi mulai tahun 2004 kami harus bertindak seolah-olah C99 selalu berisi teks ini.int (*myarray)[N] = malloc(sizeof(*myarray));
.N
, tetapi dalam sebagian besar kasus jika Anda menggunakan,malloc
Anda hanya tahu pada waktu proses.Pertanyaan ini, meskipun agak tua, membutuhkan beberapa tolok ukur, karena menanyakan cara yang tidak paling idiomatis, atau cara yang dapat ditulis dalam jumlah baris paling sedikit, tetapi cara tercepat . Dan konyol untuk menjawab pertanyaan itu tanpa pengujian yang sebenarnya. Jadi saya membandingkan empat solusi, memset vs. std :: fill vs. ZERO dari jawaban AnT vs solusi yang saya buat dengan menggunakan AVX intrinsics.
Perhatikan bahwa solusi ini tidak umum, ini hanya berfungsi pada data 32 atau 64 bit. Beri komentar jika kode ini melakukan kesalahan.
Saya tidak akan mengklaim bahwa ini adalah metode tercepat, karena saya bukan ahli pengoptimalan tingkat rendah. Melainkan merupakan contoh implementasi dependen arsitektur yang benar yang lebih cepat daripada memset.
Sekarang, ke hasilnya. Saya menghitung kinerja untuk array ukuran 100 int dan panjang, baik secara statis maupun dinamis, tetapi dengan pengecualian D3D, yang melakukan penghapusan kode mati pada array statis, hasilnya sangat sebanding, jadi saya hanya akan menampilkan kinerja array dinamis. Penandaan waktu adalah ms untuk 1 juta iterasi, menggunakan fungsi jam presisi rendah time.h.
clang 3.8 (Menggunakan frontend clang-cl, flag pengoptimalan = / OX / arch: AVX / Oi / Ot)
gcc 5.1.0 (tanda pengoptimalan: -O3 -march = native -mtune = native -mavx):
msvc 2015 (tanda pengoptimalan: / OX / arch: AVX / Oi / Ot):
Ada banyak hal menarik yang terjadi di sini: llvm kill gcc, optimasi jerawatan khas MSVC (ia melakukan penghapusan kode mati yang mengesankan pada array statis dan kemudian memiliki kinerja yang buruk untuk diisi). Meskipun implementasi saya jauh lebih cepat, ini mungkin hanya karena ia mengenali bahwa pembersihan bit memiliki overhead yang jauh lebih sedikit daripada operasi pengaturan lainnya.
Penerapan Clang patut dilihat, karena jauh lebih cepat. Beberapa pengujian tambahan menunjukkan bahwa memset-nya sebenarnya dikhususkan untuk memset nol - bukan nol untuk 400 byte array jauh lebih lambat (~ 220ms) dan sebanding dengan gcc. Namun, memset bukan nol dengan array 800 byte tidak membuat perbedaan kecepatan, yang mungkin mengapa dalam kasus itu, memset mereka memiliki kinerja yang lebih buruk daripada implementasi saya - spesialisasi hanya untuk array kecil, dan cuttoff tepat sekitar 800 byte. Perhatikan juga bahwa gcc 'fill' dan 'ZERO' tidak dioptimalkan untuk memset (melihat kode yang dihasilkan), gcc hanya menghasilkan kode dengan karakteristik performa yang identik.
Kesimpulan: memset tidak benar-benar dioptimalkan untuk tugas ini sebagaimana orang akan berpura-pura (jika tidak, memset gcc dan msvc dan llvm akan memiliki kinerja yang sama). Jika kinerja penting, maka memset tidak boleh menjadi solusi akhir, terutama untuk larik berukuran sedang yang canggung ini, karena tidak dikhususkan untuk pembersihan bit, dan tidak dioptimalkan secara manual lebih baik daripada yang dapat dilakukan oleh kompiler sendiri.
sumber
a
masuk ke dalam register. Setelah itu, dia mengulang semua blok 32 byte, yang harus sepenuhnya ditimpa menggunakan pointer arithmetics ((float *)((a)+x)
). Dua intrinsik (dimulai dengan_mm256
) hanya membuat register 32byte yang diinisialisasi nol dan menyimpannya ke penunjuk saat ini. Ini adalah 3 baris pertama. Sisanya hanya menangani semua kasus khusus di mana blok 32byte terakhir tidak boleh ditimpa sepenuhnya. Ini lebih cepat karena vektorisasi. - Saya harap itu membantu.Dari
memset()
:Kamu bisa memakai
sizeof(myarray)
jika ukuranmyarray
diketahui pada waktu kompilasi. Jika tidak, jika Anda menggunakan larik berukuran dinamis, seperti yang diperoleh melaluimalloc
ataunew
, Anda perlu melacak panjangnya.sumber
sizeof
selalu dievaluasi pada waktu kompilasi (dan tidak dapat digunakan dengan VLA). Di C99, ini bisa menjadi ekspresi runtime dalam kasus VLA.c
danc++
. Saya mengomentari jawaban Alex, yang mengatakan "Anda dapat menggunakan sizeof (myarray) jika ukuran myarray diketahui pada saat kompilasi".Kamu bisa memakai
memset
, tetapi hanya karena pilihan tipe kami dibatasi untuk tipe integral.Dalam kasus umum di C masuk akal untuk mengimplementasikan makro
Ini akan memberi Anda fungsionalitas seperti C ++ yang akan memungkinkan Anda "menyetel ulang ke nol" serangkaian objek jenis apa pun tanpa harus menggunakan peretasan seperti
memset
. Pada dasarnya, ini adalah C analog dari template fungsi C ++, kecuali Anda harus menentukan argumen type secara eksplisit.Selain itu, Anda dapat membuat "template" untuk array yang tidak membusuk
Dalam contoh Anda, ini akan diterapkan sebagai
Perlu juga dicatat bahwa khusus untuk objek dengan tipe skalar, seseorang dapat mengimplementasikan makro tipe-independen
dan
mengubah contoh di atas menjadi
sumber
;
setelahwhile(0)
, sehingga orang dapat meneleponZERO(a,n);
, +1 jawaban bagusdo{}while(0)
idiom membutuhkan no;
dalam definisi makro. Tetap.Untuk deklarasi statis, saya pikir Anda bisa menggunakan:
Untuk deklarasi dinamis, saya menyarankan cara yang sama:
memset
sumber
zero(myarray);
adalah semua yang Anda butuhkan di C ++.Tambahkan saja ini ke tajuk:
sumber
zero
ini juga benar untuk misalnyaT=char[10]
seperti yang dapat terjadi ketikaarr
argumennya adalah larik multidimensi misalnyachar arr[5][10]
.ARRAY_SIZE
makro, yang memberikan ukuran yang salah jika digunakan pada array multidimensi, nama yang lebih baik mungkin adalahARRAY_DIM<n>_SIZE
.Inilah fungsi yang saya gunakan:
Anda bisa menyebutnya seperti ini:
Di atas lebih banyak cara C ++ 11 daripada menggunakan memset. Anda juga mendapatkan kesalahan waktu kompilasi jika Anda menggunakan array dinamis dengan menentukan ukurannya.
sumber