Saya suka banyak vektor. Mereka bagus dan cepat. Tapi saya tahu benda yang disebut valarray ini ada. Mengapa saya menggunakan valarray bukan vektor? Saya tahu valarrays memiliki sedikit gula sintaksis, tetapi selain itu, kapan mereka berguna?
Saya suka banyak vektor. Mereka bagus dan cepat. Tapi saya tahu benda yang disebut valarray ini ada. Mengapa saya menggunakan valarray bukan vektor? Saya tahu valarrays memiliki sedikit gula sintaksis, tetapi selain itu, kapan mereka berguna?
Jawaban:
Valarrays (array nilai) dimaksudkan untuk membawa beberapa kecepatan Fortran ke C ++. Anda tidak akan membuat pointer valarray sehingga kompiler dapat membuat asumsi tentang kode dan mengoptimalkannya dengan lebih baik. (Alasan utama bahwa Fortran begitu cepat adalah karena tidak ada tipe pointer sehingga tidak ada pointer alias.)
Valarrays juga memiliki kelas-kelas yang memungkinkan Anda untuk mengirisnya dengan cara yang cukup mudah meskipun bagian dari standar itu dapat menggunakan sedikit lebih banyak pekerjaan. Mengubah ukuran mereka merusak dan mereka kekurangan iterator.
Jadi, jika itu angka yang Anda gunakan dan kenyamanan bukan yang terpenting gunakan valarrays. Kalau tidak, vektor jauh lebih nyaman.
valarray
adalah jenis anak yatim yang lahir di tempat yang salah pada waktu yang salah. Ini merupakan upaya optimasi, cukup khusus untuk mesin yang digunakan untuk matematika tugas berat ketika ditulis - khususnya, prosesor vektor seperti Crays.
Untuk prosesor vektor, yang biasanya ingin Anda lakukan adalah menerapkan satu operasi ke seluruh array, lalu menerapkan operasi berikutnya ke seluruh array, dan seterusnya hingga Anda melakukan semua yang perlu Anda lakukan.
Namun, kecuali Anda berurusan dengan array yang cukup kecil, itu cenderung bekerja dengan buruk dengan caching. Pada kebanyakan mesin modern, apa yang umumnya Anda sukai (sebisa mungkin) adalah memuat bagian dari array, melakukan semua operasi yang Anda inginkan, kemudian pindah ke bagian berikutnya dari array.
valarray
juga diharapkan untuk menghilangkan kemungkinan aliasing, yang (setidaknya secara teoritis) memungkinkan kompiler meningkatkan kecepatan karena lebih bebas untuk menyimpan nilai dalam register. Pada kenyataannya, bagaimanapun, saya sama sekali tidak yakin bahwa implementasi nyata memanfaatkan ini ke tingkat yang signifikan. Saya curiga ini semacam masalah ayam-dan-telur - tanpa dukungan kompiler, itu tidak menjadi populer, dan selama itu tidak populer, tidak ada yang akan bersusah payah mengerjakan kompiler mereka untuk mendukungnya.
Ada juga berbagai kelas tambahan yang membingungkan untuk digunakan dengan valarray. Anda mendapatkan slice
, slice_array
, gslice
dan gslice_array
bermain dengan potongan-potongan dari valarray
, dan membuatnya bertindak seperti array multi-dimensi. Anda juga mask_array
dapat "menutupi" operasi (mis. Tambahkan item dalam x ke y, tetapi hanya pada posisi di mana z bukan nol). Untuk membuat lebih dari penggunaan sepele valarray
, Anda harus belajar banyak tentang kelas tambahan ini, beberapa di antaranya cukup kompleks dan tidak ada yang tampaknya (setidaknya bagi saya) didokumentasikan dengan sangat baik.
Intinya: walaupun memiliki momen-momen cemerlang, dan dapat melakukan beberapa hal dengan cukup rapi, ada juga beberapa alasan yang sangat bagus bahwa itu (dan hampir pasti akan tetap) tidak jelas.
Sunting (delapan tahun kemudian, pada 2017): Beberapa pendahulunya telah menjadi usang setidaknya sampai tingkat tertentu. Sebagai contoh, Intel telah mengimplementasikan versi valarray yang dioptimalkan untuk kompilernya. Ini menggunakan Intel Integrated Performance Primitives (Intel IPP) untuk meningkatkan kinerja. Meskipun peningkatan kinerja yang tepat tidak diragukan lagi bervariasi, tes cepat dengan kode sederhana menunjukkan peningkatan kecepatan 2: 1, dibandingkan dengan kode identik yang dikompilasi dengan implementasi "standar" valarray
.
Jadi, sementara saya tidak sepenuhnya yakin bahwa programmer C ++ akan mulai menggunakan valarray
dalam jumlah besar, setidaknya ada beberapa keadaan di mana ia dapat memberikan peningkatan kecepatan.
Selama standarisasi C ++ 98, valarray dirancang untuk memungkinkan beberapa jenis perhitungan matematika cepat. Namun, sekitar waktu itu Todd Veldhuizen menemukan template ekspresi dan menciptakan blitz ++ , dan teknik template-meta serupa ditemukan, yang membuat valarrays cukup usang sebelum standar bahkan dirilis. IIRC, pengusul asli valarray meninggalkannya setengah jalan ke standardisasi, yang (jika benar) juga tidak membantunya.
ISTR bahwa alasan utama itu tidak dihapus dari standar adalah bahwa tidak ada yang meluangkan waktu untuk mengevaluasi masalah secara menyeluruh dan menulis proposal untuk menghapusnya.
Perlu diingat, bagaimanapun, bahwa semua ini hanya diingat secara samar-samar. Ambil ini dengan sebutir garam dan berharap seseorang memperbaiki atau mengkonfirmasi ini.
Saya tahu valarrays memiliki gula sintaksis
Saya harus mengatakan bahwa saya pikir tidak std::valarrays
banyak mengandung gula sintaksis. Sintaksnya berbeda, tetapi saya tidak akan menyebut perbedaan itu "gula." APInya aneh. Bagian pada std::valarray
s di Bahasa Pemrograman C ++ menyebutkan API yang tidak biasa ini dan fakta bahwa, karena std::valarray
s diharapkan sangat dioptimalkan, pesan kesalahan apa pun yang Anda dapatkan saat menggunakannya mungkin tidak akan intuitif.
Karena penasaran, sekitar satu tahun yang lalu saya diadu std::valarray
melawan std::vector
. Saya tidak lagi memiliki kode atau hasil yang tepat (walaupun seharusnya tidak sulit untuk menulis sendiri). Menggunakan GCC saya memang mendapatkan sedikit manfaat kinerja ketika menggunakan std::valarray
untuk matematika sederhana, tetapi tidak untuk implementasi saya untuk menghitung standar deviasi (dan, tentu saja, standar deviasi tidak begitu rumit, sejauh matematika berjalan). Saya menduga bahwa operasi pada setiap item dalam permainan besar ( CATATAN , mengikuti saran dari musiphil , saya telah berhasil mendapatkan kinerja yang hampir sama dari std::vector
lebih baik dengan cache daripada operasi pada std::valarray
s. vector
dan valarray
).
Pada akhirnya, saya memutuskan untuk menggunakan std::vector
sambil memperhatikan hal-hal seperti alokasi memori dan pembuatan objek sementara.
Keduanya std::vector
dan std::valarray
menyimpan data dalam blok yang berdekatan. Namun, mereka mengakses data itu menggunakan pola yang berbeda, dan yang lebih penting, API untuk std::valarray
mendorong pola akses yang berbeda dari API untuk std::vector
.
Sebagai contoh standar deviasi, pada langkah tertentu saya perlu menemukan rata-rata koleksi dan perbedaan antara nilai setiap elemen dan rata-rata.
Untuk itu std::valarray
, saya melakukan sesuatu seperti:
std::valarray<double> original_values = ... // obviously I put something here
double mean = original_values.sum() / original_values.size();
std::valarray<double> temp(mean, original_values.size());
std::valarray<double> differences_from_mean = original_values - temp;
Saya mungkin lebih pintar dengan std::slice
atau std::gslice
. Sudah lebih dari lima tahun sekarang.
Karena std::vector
, saya melakukan sesuatu seperti:
std::vector<double> original_values = ... // obviously, I put something here
double mean = std::accumulate(original_values.begin(), original_values.end(), 0.0) / original_values.size();
std::vector<double> differences_from_mean;
differences_from_mean.reserve(original_values.size());
std::transform(original_values.begin(), original_values.end(), std::back_inserter(differences_from_mean), std::bind1st(std::minus<double>(), mean));
Hari ini saya pasti akan menulisnya secara berbeda. Jika tidak ada yang lain, saya akan mengambil keuntungan dari C ++ 11 lambdas.
Jelas bahwa kedua cuplikan kode ini melakukan hal yang berbeda. Sebagai std::vector
contoh , contoh tidak membuat koleksi perantara seperti std::valarray
contoh. Namun, saya pikir itu adil untuk membandingkan mereka karena perbedaan terkait dengan perbedaan antara std::vector
dan std::valarray
.
Ketika saya menulis jawaban ini, saya curiga bahwa mengurangi nilai elemen dari dua std::valarray
s (baris terakhir dalam std::valarray
contoh) akan lebih sedikit cache-friendly daripada baris yang sesuai dalam std::vector
contoh (yang kebetulan juga menjadi baris terakhir).
Ternyata, itu
std::valarray<double> original_values = ... // obviously I put something here
double mean = original_values.sum() / original_values.size();
std::valarray<double> differences_from_mean = original_values - mean;
Melakukan hal yang sama dengan std::vector
contoh, dan memiliki kinerja yang hampir sama. Pada akhirnya, pertanyaannya adalah API mana yang Anda sukai.
std::vector
akan bermain lebih baik dengan cache daripada a std::valarray
; mereka berdua mengalokasikan satu blok memori yang berdekatan untuk elemen-elemen mereka.
valarray
contoh Anda di atas, Anda tidak harus membangun temp
valarray
objek, tetapi Anda bisa saja selesai std::valarray<double> differences_from_mean = original_values - mean;
, dan kemudian perilaku cache harus mirip dengan vector
contoh tersebut. (Omong-omong, jika mean
benar-benar int
, tidak double
, Anda mungkin perlu static_cast<double>(mean)
.)
valarray
. Saya perlu melihat apakah itu meningkatkan kinerja. Adapun mean
menjadi int
: itu adalah kesalahan. Saya awalnya menulis contoh menggunakan int
s, dan kemudian menyadari bahwa mean
kemudian akan sangat jauh dari rata-rata yang sebenarnya karena pemotongan. Tetapi saya melewatkan beberapa perubahan yang diperlukan pada putaran pengeditan pertama saya.
valarray seharusnya membiarkan beberapa FORTRAN memproses vektor kebaikan pada C ++. Entah bagaimana dukungan kompiler yang diperlukan tidak pernah benar-benar terjadi.
Buku-buku Josuttis berisi beberapa komentar menarik (agak meremehkan) tentang valarray (di sini dan di sini ).
Namun, Intel sekarang tampaknya meninjau kembali valarray dalam rilis kompiler terbaru mereka (mis. Lihat slide 9 ); ini adalah perkembangan yang menarik mengingat bahwa set instruksi SIMD SSE 4-arah mereka akan segera diikuti oleh instruksi AVX 8-arah dan Larrabee 16-arah dan untuk kepentingan portabilitas, akan lebih baik untuk membuat kode dengan abstraksi seperti valarray dari (katakanlah) intrinsik.
Saya menemukan satu penggunaan yang baik untuk valarray. Ini untuk menggunakan valarray seperti array numpy.
auto x = linspace(0, 2 * 3.14, 100);
plot(x, sin(x) + sin(3.f * x) / 3.f + sin(5.f * x) / 5.f);
Kita bisa menerapkan di atas dengan valarray.
valarray<float> linspace(float start, float stop, int size)
{
valarray<float> v(size);
for(int i=0; i<size; i++) v[i] = start + i * (stop-start)/size;
return v;
}
std::valarray<float> arange(float start, float step, float stop)
{
int size = (stop - start) / step;
valarray<float> v(size);
for(int i=0; i<size; i++) v[i] = start + step * i;
return v;
}
string psstm(string command)
{//return system call output as string
string s;
char tmp[1000];
FILE* f = popen(command.c_str(), "r");
while(fgets(tmp, sizeof(tmp), f)) s += tmp;
pclose(f);
return s;
}
string plot(const valarray<float>& x, const valarray<float>& y)
{
int sz = x.size();
assert(sz == y.size());
int bytes = sz * sizeof(float) * 2;
const char* name = "plot1";
int shm_fd = shm_open(name, O_CREAT | O_RDWR, 0666);
ftruncate(shm_fd, bytes);
float* ptr = (float*)mmap(0, bytes, PROT_WRITE, MAP_SHARED, shm_fd, 0);
for(int i=0; i<sz; i++) {
*ptr++ = x[i];
*ptr++ = y[i];
}
string command = "python plot.py ";
string s = psstm(command + to_string(sz));
shm_unlink(name);
return s;
}
Kami juga membutuhkan skrip python.
import sys, posix_ipc, os, struct
import matplotlib.pyplot as plt
sz = int(sys.argv[1])
f = posix_ipc.SharedMemory("plot1")
x = [0] * sz
y = [0] * sz
for i in range(sz):
x[i], y[i] = struct.unpack('ff', os.read(f.fd, 8))
os.close(f.fd)
plt.plot(x, y)
plt.show()
Standar C ++ 11 mengatakan:
Kelas array valarray didefinisikan bebas dari bentuk-bentuk aliasing tertentu, sehingga memungkinkan operasi pada kelas-kelas ini dioptimalkan.
Lihat C ++ 11 26.6.1-2.
Dengan std::valarray
Anda dapat menggunakan notasi matematika standar seperti di v1 = a*v2 + v3
luar kotak. Ini tidak dimungkinkan dengan vektor kecuali Anda menentukan operator Anda sendiri.
std :: valarray ditujukan untuk tugas-tugas numerik yang berat, seperti Dinamika Cairan Komputasi atau Dinamika Struktur Komputasi, di mana Anda memiliki array dengan jutaan, terkadang puluhan juta item, dan Anda mengulanginya dalam satu lingkaran dengan jutaan catatan waktu. Mungkin hari ini std :: vector memiliki kinerja yang sebanding tetapi, sekitar 15 tahun yang lalu, valarray hampir wajib jika Anda ingin menulis pemecah angka yang efisien.