C ++ valarray ve vektör


159

Vektörleri çok seviyorum. Şık ve hızlılar. Ama bir valarray denen şeyin var olduğunu biliyorum. Neden vektör yerine valarray kullanayım? Valarrailerin bazı sözdizimsel şekerleri olduğunu biliyorum, ama bunun dışında ne zaman yararlılar?


2
Geçen gün de bunu düşünüyordum. Bildiğim kadarıyla, bu gerçekten uzman matematik vektörü kadar.
GManNickG

Valarray ifade şablonları yapmaz mı?
Mooing Duck

Fizikçi Ulrich Mutze valarray burada ve burada
cankurtaran

Yanıtlar:


70

Valarrays (değer dizileri), Fortran'ın bazı hızlarını C ++ 'a getirmeyi amaçlamaktadır. Derleyicinin kod hakkında varsayımlarda bulunabilmesi ve onu daha iyi optimize edebilmesi için bir işaretçi valarray yapmazsınız. (Fortran'ın bu kadar hızlı olmasının ana nedeni, işaretçi türünün olmamasıdır, bu nedenle işaretçi takma adı olamaz.)

Valarrays ayrıca, standardın bir kısmı biraz daha fazla iş kullanabilmesine rağmen, bunları oldukça kolay bir şekilde dilimlemenize izin veren sınıflara sahiptir. Onları yeniden boyutlandırmak yıkıcıdır ve yineleyicilerden yoksundurlar.

Yani, üzerinde çalıştığınız rakamlar ve rahatlık valarrays kullanmak kadar önemli değil. Aksi takdirde, vektörler çok daha uygundur.


11
İşaretçilerden kaçınmak için tasarlanmamıştır. C ++ 11, valarray'da yineleyicileri kendilerine döndüren begin () ve end () öğelerini tanımlar
Mohamed El-Nakib

3
@ user2023370: Bu yüzden birçok Fortran kullanıcısı Fortran 77'yi tercih ediyor. :)
Michael

152

valarrayyanlış yerde yanlış zamanda doğmuş bir yetimdir. Bu, özellikle yazılırken ağır matematik için kullanılan makineler, özellikle de Crays gibi vektör işlemciler için kullanılan bir optimizasyon girişimidir.

Bir vektör işlemcisi için, genel olarak yapmak istediğiniz şey, tüm diziye tek bir işlem uygulamak, ardından bir sonraki işlemi tüm diziye uygulamak ve böylece yapmanız gereken her şeyi yapana kadar devam etmekti.

Bununla birlikte, oldukça küçük dizilerle uğraşmadıkça, bu önbellekleme ile zayıf çalışma eğilimindedir. Çoğu modern makinede, genel olarak (mümkün olduğu ölçüde) tercih edeceğiniz şey, dizinin bir bölümünü yüklemek, üzerinde yapacağınız tüm işlemleri yapmak, ardından dizinin bir sonraki bölümüne geçmek olacaktır.

valarrayayrıca (en azından teorik olarak) derleyicinin hızı geliştirmesine izin veren herhangi bir örtüşme olasılığını ortadan kaldırması gerekir, çünkü değerleri kayıtlarda saklamak daha ücretsizdir. Ancak gerçekte, herhangi bir gerçek uygulamanın bundan önemli ölçüde faydalandığından emin değilim. Bunun tavuk ve yumurta gibi bir sorun olduğundan şüpheliyim - derleyici desteği olmadan popüler olmadı ve popüler olmadığı sürece kimse onu derlemek için derleyicileri üzerinde çalışma sorununa gitmeyecek.

Ayrıca valarray ile kullanmak için şaşırtıcı (tam anlamıyla) bir dizi yardımcı sınıf var. Sen almak slice, slice_array, gsliceve gslice_arraybir parçaları ile oynamak valarrayve bu çok boyutlu dizide gibi hareket edin. Ayrıca mask_arraybir işlemi "maskelemek" de mümkündür (örneğin, x'den y'ye, ancak z'nin sıfır olmadığı konumlarda öğeler ekleyin). Önemsiz kullanmaktan daha fazlasını yapmakvalarray , bazıları oldukça karmaşık olan ve hiçbiri (en azından benim için) çok iyi belgelenmiş gibi görünmeyen bu yardımcı sınıflar hakkında çok şey öğrenmelisiniz.

Alt satır: Parlaklık anları olsa da ve bazı şeyleri oldukça düzgün bir şekilde yapabilse de, belirsiz olması (ve neredeyse kesinlikle kalacağı) için bazı çok iyi nedenler de vardır.

Düzenleme (sekiz yıl sonra, 2017'de): Öncekilerden bazıları en azından bir dereceye kadar eskimiş hale geldi. Bir örnekte Intel, derleyicileri için optimize edilmiş bir valarray sürümü uyguladı. Performansı artırmak için Intel Tümleşik Performans İlkeleri'ni (Intel IPP) kullanır. Her ne kadar kesin performans artışı şüphesiz değişse de, basit kodlu hızlı bir test, "standart" uygulama ile derlenen aynı koda kıyasla hızda 2: 1'lik bir iyileşme gösterir.valarray .

Yani, tamamen C ++ programcılarının valarrayçok sayıda kullanmaya başlayacağına ikna olmasam da, bir hız iyileştirmesi sağlayabileceği en az bazı durumlar var.


1
Valarray içinde keyfi nesne türlerinin depolanmasına özellikle izin verilmiyor mu?
user541686

6
@Mehrdad: Evet - [Numeric.Requirements] 'da (oldukça uzun) bir kısıtlamalar listesi var. Sadece birkaç örnek için, tüm soyut sınıflar ve istisnalar yasaktır. Ayrıca (örneğin) kopya yapısı ile bir dizi varsayılan yapı ve ardından atama arasında bir eşdeğerlik gerektirir.
Jerry Coffin

@JerryCoffin bu çok korkutucu. kullanmayacağımıza söz veriyoruz.
Hani Goc

4
Korkuya dayanarak buna karar vermezdim. Yasakladığı özellikleri kullanan öğeleri saklamanız gerekip gerekmediğine bağlı olarak karar veririm.
Jerry Coffin

3
@annoying_squid: Ekleyeceğiniz daha spesifik ve (doğru) doğru bilgileriniz varsa, lütfen bunu gösteren bir cevap eklemekten çekinmeyin. Şu anda olduğu gibi, yorumunuz yararlı bilgiler eklemiyor gibi görünüyor.
Jerry Coffin

39

C ++ 98'in standardizasyonu sırasında valarray, bir tür hızlı matematiksel hesaplamalara izin verecek şekilde tasarlanmıştır. Ancak, o zaman zarfında Todd Veldhuizen ifade şablonlarını icat etti ve blitz ++ yarattı ve benzer şablon-meta teknikleri icat edildi, bu da standartların piyasaya sürülmesinden önce valarraları hemen hemen eskimiş hale getirdi. Valarray'ın orijinal önerici (ler) i IIRC, standardizasyonun yarısını terk etti (ki eğer doğruysa) da yardımcı olmadı.

Standarttan çıkarılmamasının ana nedeninin, konuyu kapsamlı bir şekilde değerlendirmek ve kaldırmak için bir teklif yazmak için hiç zaman ayırmamasıdır.

Bununla birlikte, tüm bunların belirsiz bir şekilde hatırlandığını unutmayın. Bunu bir tuz tanesi ile alın ve umarım birisi bunu düzeltir veya onaylar.


ifade şablonları da Vandevoorde'a eşit şekilde yatırılabilir, değil mi?
Nikos Athanasiou

@Nikos: Bildiğimden değil. Yine de yanılmış olabilirim. Bu okumadan yana neyin var?
sbi

1
"C ++ Şablonları - tam rehber" kitabında bahsedildi, Bence her ikisi de onları bağımsız olarak icat kabul edilir .
Nikos Athanasiou

27

Valarrayların sözdizimsel şekeri olduğunu biliyorum

std::valarraysSözdizimsel şeker yolunda fazla bir şey olmadığını düşündüğümü söylemeliyim . Sözdizimi farklıdır, ancak farkı "şeker" olarak adlandırmazdım. API garip. Bölüm std::valarrayiçinde s Dili Programlama C ++ bu sıradışı API ve gerçeğini bahseder, o yanastd::valarray ler son derece optimize edilmesi bekleniyor, herhangi bir hata iletileri muhtemelen olmayan sezgisel olacak bunları kullanırken olsun.

Meraktan, yaklaşık bir yıl önce std::valarraykarşı çıktım std::vector. (Kendi yazmak zor olmamasına rağmen) artık kodu veya kesin sonuçları var. GCC ı kullanarak yaptığı kullanırken biraz iyi performansı elde std::valarraybenim uygulamaları standart sapmayı hesaplamak için basit matematik için değil (tabii ve standart sapma değil karmaşık olduğunu, bildiğim kadarıyla matematik gider gibi). Her bir öğedeki işlemlerin büyük bir std::vectorönbellekle std::valarrays işlemlerinden daha iyi oynadığından şüpheleniyorum . ( NOT , musiphil'in tavsiyelerini takiben , vectorve neredeyse aynı performansı elde etmeyi başardım valarray).

Sonunda, std::vectorbellek ayırma ve geçici nesne oluşturma gibi şeylere çok dikkat ederek kullanmaya karar verdim .


Her ikisi de std::vector ve std::valarraybitişik bloktaki verileri depolar. Bununla birlikte, bu verilere farklı kalıplar kullanarak ve daha da önemlisi, API için API için std::valarrayfarklı erişim kalıplarını teşvik ederler std::vector.

Standart sapma örneği için, belirli bir adımda koleksiyonun ortalamasını ve her bir öğenin değeri ile ortalama arasındaki farkı bulmam gerekiyordu.

İçin std::valarray , ben gibi bir şey yaptım:

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;

Daha zeki olabilirim std::sliceVeyastd::gslice . Beş yıldan fazla oldu.

İçin std::vector , bir şeyler yaptım:

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

Bugün bunu kesinlikle farklı yazardım. Başka bir şey değilse, C ++ 11 lambdas yararlanmak.

Bu iki kod parçacığının farklı şeyler yaptığı açıktır. Birincisi, std::vectorörnek , örnekte olduğu gibi bir ara toplama std::valarrayyapmaz. Ancak, onları karşılaştırmanın adil olduğunu düşünüyorum çünkü farklılıklar std::vectorvestd::valarray .

Bu cevabı yazdığımda, elemanların değerini iki std::valarraysaniyeden (std::valarray örnekteki ) çıkarmanın örnekteki karşılık gelen satırdan std::vector(son satır da olur ) daha az önbellek dostu olacağından .

Ancak, ortaya çıktı ki

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;

Örnekle aynı şeyi yapar std::vectorve neredeyse aynı performansa sahiptir. Sonuçta, soru hangi API'yı tercih ettiğinizdir.


std::vectorA'nın önbelleklerle neden daha iyi oynayacağına dair hiçbir neden düşünemiyorum std::valarray; her ikisi de öğeleri için tek bir bitişik bellek bloğu tahsis eder.
musiphil

1
@musiphil Yanıtım yorum yapmak için çok uzun sürdü, bu yüzden yanıtı güncelledim.
Max Lybbert

1
Sizin için valarrayyukarıdaki örnekte, bir inşa etmek yoktu temp valarraynesne, ama sadece yapmış olabilir std::valarray<double> differences_from_mean = original_values - mean;ve sonra önbellek davranış benzer olmalıdır vectorörneğin. (Bu arada, eğer meangerçekten intdeğilse double, ihtiyacınız olabilir static_cast<double>(mean).)
musiphil

Temizlemek için öneri için teşekkürler valarray. Bunun performansı iyileştirip iyileştirmediğini görmem gerek. Gelince meanolmak int: o bir hataydı. Başlangıçta örneği ints kullanarak yazdım ve daha meansonra bunun kesilme nedeniyle gerçek ortalamadan çok uzak olacağını fark ettim . Ancak ilk düzenlemelerimde birkaç gerekli değişikliği kaçırdım.
Max Lybbert

@musiphil Haklısın; bu değişiklik örnek kodu neredeyse aynı performansa getirdi.
Max Lybbert

23

valarray'ın bazı FORTRAN vektör işleme iyiliğinin C ++ 'da kaymasına izin vermesi gerekiyordu. Bir şekilde gerekli derleyici desteği hiç olmadı.

Josuttis kitapları valarray hakkında ( burada ve burada ) bazı ilginç (biraz aşağılayıcı) yorumlar içeriyor ) .

Bununla birlikte, Intel şimdi derleyici sürümlerinde valarray'i yeniden ziyaret ediyor gibi görünüyor (ör. Slayt 9 ); 4 yönlü SIMD SSE komut setinin 8 yollu AVX ve 16 yollu Larrabee talimatları ile birleştirilmek üzere olduğu göz önüne alındığında bu ilginç bir gelişmedir ve taşınabilirlik açısından, gerçeklerden daha valarray.


16

Valarray için iyi bir kullanım buldum. Valarray'ı numpy dizileri gibi kullanmaktır.

auto x = linspace(0, 2 * 3.14, 100);
plot(x, sin(x) + sin(3.f * x) / 3.f + sin(5.f * x) / 5.f);

resim açıklamasını buraya girin

Yukarıda valarray ile uygulayabiliriz.

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

Ayrıca, python betiğine ihtiyacımız var.

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

2
Bugün işyerinde valarray'ı öğrendiğimde tam olarak aynı düşüncelere sahiptim. Şu andan itibaren c ++ matematik işleme sorunları için kod bir matematik perspektifinden stand altında daha basit görünüyor çünkü valarray kullanacağını düşünüyorum.
Zachary Kraus

8

C ++ 11 standardı şunları söylüyor:

Valarray dizi sınıflarının belirli kenar yumuşatma biçimlerinden arınmış olduğu tanımlanır, böylece bu sınıflar üzerindeki işlemlerin optimize edilmesine izin verilir.

Bkz. C ++ 11 26.6.1-2.


Standardın hangi formları tanımladığını varsaydığından, bunları teklif edebilir misiniz? Ayrıca, bunlar kodlama hileleri kullanılarak mı uygulanıyor, yoksa dilin başka bir yerinde kuralların örtüşme kurallarının derleyici tabanlı istisnaları mı?
underscore_d

2

İle std::valarraysenin gibi standart matematiksel gösterim kullanabilirsiniz v1 = a*v2 + v3kutusunun out. Kendi operatörlerinizi tanımlamadığınız sürece bu vektörlerle mümkün değildir.


0

std :: valarray, milyonlarca, bazen on milyonlarca öğe içeren dizilere sahip olduğunuz Hesaplama Sıvısı Dinamiği veya Hesaplama Yapısı Dinamiği gibi ağır sayısal görevler için tasarlanmıştır ve bunları milyonlarca zamana sahip bir döngüde yineleyin. Belki bugün std :: vector karşılaştırılabilir bir performansa sahiptir, ancak 15 yıl önce, verimli bir sayısal çözücü yazmak istiyorsanız valarray neredeyse zorunluydu.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.