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?
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?
Yanıtlar:
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.
valarray
yanlış 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.
valarray
ayrı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
, gslice
ve gslice_array
bir parçaları ile oynamak valarray
ve bu çok boyutlu dizide gibi hareket edin. Ayrıca mask_array
bir 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.
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.
Valarrayların sözdizimsel şekeri olduğunu biliyorum
std::valarrays
Sö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::valarray
iç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::valarray
karşı çı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::valarray
benim 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 ( NOT , musiphil'in tavsiyelerini takiben , std::vector
önbellekle std::valarray
s işlemlerinden daha iyi oynadığından şüpheleniyorum . vector
ve neredeyse aynı performansı elde etmeyi başardım valarray
).
Sonunda, std::vector
bellek ayırma ve geçici nesne oluşturma gibi şeylere çok dikkat ederek kullanmaya karar verdim .
Her ikisi de std::vector
ve std::valarray
bitişik bloktaki verileri depolar. Bununla birlikte, bu verilere farklı kalıplar kullanarak ve daha da önemlisi, API için API için std::valarray
farklı 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::slice
Veyastd::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::valarray
yapmaz. Ancak, onları karşılaştırmanın adil olduğunu düşünüyorum çünkü farklılıklar std::vector
vestd::valarray
.
Bu cevabı yazdığımda, elemanların değerini iki std::valarray
saniyeden (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::vector
ve neredeyse aynı performansa sahiptir. Sonuçta, soru hangi API'yı tercih ettiğinizdir.
std::vector
A'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.
valarray
yukarıdaki örnekte, bir inşa etmek yoktu temp
valarray
nesne, 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 mean
gerçekten int
değilse double
, ihtiyacınız olabilir static_cast<double>(mean)
.)
valarray
. Bunun performansı iyileştirip iyileştirmediğini görmem gerek. Gelince mean
olmak int
: o bir hataydı. Başlangıçta örneği int
s kullanarak yazdım ve daha mean
sonra 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.
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.
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);
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()
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.
İle std::valarray
senin gibi standart matematiksel gösterim kullanabilirsiniz v1 = a*v2 + v3
kutusunun out. Kendi operatörlerinizi tanımlamadığınız sürece bu vektörlerle mümkün değildir.
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.