C ++ valarray vs vektor

159

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?

rbbond
sumber
2
Baru saja merenungkan ini beberapa hari yang lalu. Sejauh yang saya tahu, itu benar-benar hanya sebagai vektor matematika khusus.
GManNickG
Bukankah valarray melakukan templat ekspresi?
Mooing Duck
Fisikawan Ulrich Mutze menyediakan kasus penggunaan untuk di valarray sini dan di sini
lifebalance

Jawaban:

70

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.

Tim Allman
sumber
11
Mereka tidak dirancang untuk menghindari petunjuk. C ++ 11 mendefinisikan begin () dan end () dalam valarray yang mengembalikan iterator kepada mereka
Mohamed El-Nakib
3
@ user2023370: itu sebabnya banyak pengguna Fortran memilih Fortran 77. :)
Michael
152

valarrayadalah 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.

valarrayjuga 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, gslicedan gslice_arraybermain dengan potongan-potongan dari valarray, dan membuatnya bertindak seperti array multi-dimensi. Anda juga mask_arraydapat "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 valarraydalam jumlah besar, setidaknya ada beberapa keadaan di mana ia dapat memberikan peningkatan kecepatan.

Jerry Coffin
sumber
1
Apakah secara khusus tidak diizinkan untuk menyimpan jenis objek yang sewenang-wenang di dalam valarray?
user541686
6
@Mehrdad: Ya - ada daftar pembatasan (agak panjang) di [Numeric.Requirements]. Untuk beberapa contoh saja, semua kelas dan pengecualian abstrak dilarang. Ini juga membutuhkan kesetaraan antara (misalnya) konstruksi salin dan urutan konstruksi standar diikuti oleh penugasan.
Jerry Coffin
@ JerryCoffin sheesh itu menakutkan. kami berjanji bahwa kami tidak akan menggunakannya.
Hani Goc
4
Saya tidak akan memutuskan itu berdasarkan ketakutan. Saya akan memutuskan berdasarkan apakah Anda perlu menyimpan elemen yang menggunakan fitur yang dilarang.
Jerry Coffin
3
@annoying_squid: Jika Anda memiliki informasi yang lebih spesifik dan (Anda yakini) tambahkan, jangan ragu untuk menambahkan jawaban yang menunjukkannya. Seperti yang ada sekarang, komentar Anda tampaknya tidak menambahkan informasi yang berguna.
Jerry Coffin
39

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.

sbi
sumber
templat ekspresi dapat sama-sama dikreditkan ke Vandevoorde, kan?
Nikos Athanasiou
@ Nikos: Bukan yang saya tahu. Tapi aku bisa saja salah. Apa yang Anda sukai dari bacaan itu?
sbi
1
itu disebutkan dalam buku "C ++ Templates - Panduan lengkap", saya pikir secara umum diterima bahwa mereka berdua menciptakannya secara mandiri .
Nikos Athanasiou
27

Saya tahu valarrays memiliki gula sintaksis

Saya harus mengatakan bahwa saya pikir tidak std::valarraysbanyak mengandung gula sintaksis. Sintaksnya berbeda, tetapi saya tidak akan menyebut perbedaan itu "gula." APInya aneh. Bagian pada std::valarrays di Bahasa Pemrograman C ++ menyebutkan API yang tidak biasa ini dan fakta bahwa, karena std::valarrays 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::valarraymelawan 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::valarrayuntuk 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 std::vectorlebih baik dengan cache daripada operasi pada std::valarrays. ( CATATAN , mengikuti saran dari musiphil , saya telah berhasil mendapatkan kinerja yang hampir sama dari vectordan valarray).

Pada akhirnya, saya memutuskan untuk menggunakan std::vectorsambil memperhatikan hal-hal seperti alokasi memori dan pembuatan objek sementara.


Keduanya std::vectordan std::valarraymenyimpan data dalam blok yang berdekatan. Namun, mereka mengakses data itu menggunakan pola yang berbeda, dan yang lebih penting, API untuk std::valarraymendorong 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::sliceatau 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::vectorcontoh , contoh tidak membuat koleksi perantara seperti std::valarraycontoh. Namun, saya pikir itu adil untuk membandingkan mereka karena perbedaan terkait dengan perbedaan antara std::vectordan std::valarray.

Ketika saya menulis jawaban ini, saya curiga bahwa mengurangi nilai elemen dari dua std::valarrays (baris terakhir dalam std::valarraycontoh) akan lebih sedikit cache-friendly daripada baris yang sesuai dalam std::vectorcontoh (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::vectorcontoh, dan memiliki kinerja yang hampir sama. Pada akhirnya, pertanyaannya adalah API mana yang Anda sukai.

Max Lybbert
sumber
Saya tidak bisa memikirkan alasan mengapa a std::vectorakan bermain lebih baik dengan cache daripada a std::valarray; mereka berdua mengalokasikan satu blok memori yang berdekatan untuk elemen-elemen mereka.
musiphil
1
@musiphil Tanggapan saya terlalu panjang untuk dikomentari, jadi saya sudah memperbarui jawabannya.
Max Lybbert
1
Untuk valarraycontoh Anda di atas, Anda tidak harus membangun temp valarrayobjek, tetapi Anda bisa saja selesai std::valarray<double> differences_from_mean = original_values - mean;, dan kemudian perilaku cache harus mirip dengan vectorcontoh tersebut. (Omong-omong, jika meanbenar-benar int, tidak double, Anda mungkin perlu static_cast<double>(mean).)
musiphil
Terima kasih atas sarannya untuk membersihkan valarray. Saya perlu melihat apakah itu meningkatkan kinerja. Adapun meanmenjadi int: itu adalah kesalahan. Saya awalnya menulis contoh menggunakan ints, dan kemudian menyadari bahwa meankemudian akan sangat jauh dari rata-rata yang sebenarnya karena pemotongan. Tetapi saya melewatkan beberapa perubahan yang diperlukan pada putaran pengeditan pertama saya.
Max Lybbert
@musiphil Anda benar; perubahan itu membawa kode sampel ke kinerja yang hampir identik.
Max Lybbert
23

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.

timday
sumber
16

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);

masukkan deskripsi gambar di sini

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()
Zeta
sumber
2
Saya benar-benar memiliki pikiran yang sama persis seperti yang Anda lakukan ketika saya mengetahui tentang valarray hari ini di tempat kerja. Saya pikir mulai sekarang untuk masalah pemrosesan matematika di c ++ saya akan menggunakan valarray karena kode terlihat lebih mudah untuk dipahami dari perspektif matematika.
Zachary Kraus
8

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.

Lingxi
sumber
Karena saya menganggap Standar menentukan bentuk mana, dapatkah Anda mengutipnya? Juga, apakah ini diimplementasikan menggunakan trik pengkodean, atau mereka pengecualian berbasis kompiler untuk aturan alias di tempat lain dalam bahasa?
underscore_d
2

Dengan std::valarrayAnda dapat menggunakan notasi matematika standar seperti di v1 = a*v2 + v3luar kotak. Ini tidak dimungkinkan dengan vektor kecuali Anda menentukan operator Anda sendiri.

Paul Jurczak
sumber
0

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.

mrpivello
sumber