Sıralı sayıların verimli istikrarlı toplamı


12

Kayan nokta pozitif sayılar ( std::vector<float>, boyut ~ 1000) oldukça uzun bir listem var . Sayılar azalan düzende sıralanır. Siparişi takip ederek onları toplarsam:

for (auto v : vec) { sum += v; }

Sanırım bazı sayısal kararlılık problemim olabilir, çünkü vektörün sonuna yakın sumolandan çok daha büyük olacak v. En kolay çözüm, vektörü ters sırada hareket ettirmek olacaktır. Sorum şu: ileri durum kadar verimli mi? Daha fazla önbellek eksik mi olacak?

Başka akıllı bir çözüm var mı?


1
Hız sorusunu cevaplamak kolaydır. Kıyaslayın.
Davide Spataro

Hız doğruluktan daha mı önemli?
stark

Oldukça yinelenen değil, çok benzer bir soru: float kullanan seri toplamı
acraig5075

4
Negatif sayılara dikkat etmeniz gerekebilir.
AProgrammer

3
Yüksek hassasiyete gerçekten önem veriyorsanız, Kahan toplamına göz atın .
Max Langhof

Yanıtlar:


3

Sanırım bazı sayısal kararlılık problemlerim olabilir

Bu yüzden test edin. Şu anda varsayımsal bir probleminiz var, yani hiç problem yok.

Test ederseniz ve varsayım gerçek bir soruna dönüşürse , gerçekten düzeltmek için endişelenmelisiniz.

Kayan nokta hassas - Yani olabilir sorunlarına neden, ama bunu gerçekten başka üzerinde her şey bu öncelik önce, verileriniz için yapar olmadığını teyit edebilir.

... daha fazla önbellek eksik mi olacak?

Bin float 4Kb'dir - modern bir kitle pazarı sistemine önbellekte sığar (aklınızda başka bir platformunuz varsa, bunun ne olduğunu bize bildirin).

Tek risk, prefetcher'ın geriye doğru yineleme yaparken size yardımcı olmamasıdır, ancak elbette vektörünüz zaten önbellekte olabilir. Tam programınızın bağlamında profil oluşturana kadar bunu gerçekten belirleyemezsiniz, bu nedenle tam bir programınız olana kadar endişelenmenize gerek yoktur.

Başka akıllı bir çözüm var mı?

Sorun haline gelene kadar sorun olabilecek şeyler için endişelenmeyin. En fazla olası sorunları belirtmeye ve kodunuzu, mümkün olan en basit çözümü, daha sonra her şeyi yeniden yazmadan dikkatlice optimize edilmiş bir çözümle değiştirebileceğiniz şekilde yapılandırmaya değer.


5

I tezgah işaretlenmiş için kullanım durumunda ve sonuçları bu döngü için herhangi bir performans farkı, ileri veya geri yapmaz yönüne noktası (ekli resim).

Donanım + derleyicinizde de ölçüm yapmak isteyebilirsiniz.


Toplamı gerçekleştirmek için STL'yi kullanmak, veriler üzerinde manuel döngü kadar hızlı ancak çok daha etkileyici.

ters birikim için aşağıdakileri kullanın:

std::accumulate(rbegin(data), rend(data), 0.0f);

ileri birikim için:

std::accumulate(begin(data), end(data), 0.0f);

resim açıklamasını buraya girin


Bu web sitesi süper havalı. Sadece emin olmak için: rastgele nesli zamanlamıyorsunuz, değil mi?
Ruggero Turra

Hayır, yalnızca statedöngüdeki bölüm zamanlanır.
Davide Spataro

2

En kolay çözüm, vektörü ters sırada hareket ettirmek olacaktır. Sorum şu: ileri durum kadar verimli mi? Daha fazla önbellek eksik mi olacak?

Evet etkilidir. Donanımınızdan şube tahmini ve akıllı önbellek stratejisi sıralı erişim için ayarlanmıştır. Vektörünüzü güvenle biriktirebilirsiniz:

#include <numeric>

auto const sum = std::accumulate(crbegin(v), crend(v), 0.f);

2
Açıklayabilir misiniz? Bu bağlamda "sıralı erişim" ileri, geri veya her ikisi anlamına gelir?
Ruggero Turra

1
@RuggeroTurra Bir kaynak bulamadığım sürece yapamıyorum ve şu anda CPU veri sayfalarını okuma havasında değilim.
YSC

@RuggeroTurra Genellikle ardışık erişim ileri anlamına gelir. Tüm yarı iyi bellek ön getiricileri sıralı erişimi önler.
Diş fırçası

@ Diş fırçası, teşekkürler. Yani, geriye doğru dönersem, prensip olarak, bir performans sorunu olabilir
Ruggero Turra

Prensip olarak, tüm vektör zaten L1 önbelleğinde değilse, en azından bazı donanımlarda .
Yararsız

2

Bu amaçla, tersine yineleyiciyi herhangi bir aktarım olmadan kullanabilirsiniz std::vector<float> vec:

float sum{0.f};
for (auto rIt = vec.rbegin(); rIt!= vec.rend(); ++rIt)
{
    sum += *rit;
}

Veya aynı işi standart algortitmayı kullanarak yapın:

float sum = std::accumulate(vec.crbegin(), vec.crend(), 0.f);

Performans aynı olmalı, yalnızca vektörünüzün baypas yönünü değiştirmelidir


Yanılıyorsam beni düzeltin, ama bu OP'nin kullandığı foreach ifadesinden daha etkili olduğunu düşünüyorum, çünkü bir ek yük getiriyor. YSC, sayısal kararlılık kısmı hakkında haklıdır, tho.
Sephiroth

4
@sephiroth Hayır, herhangi bir yarı iyi derleyici, bir aralık veya yineleyici yazıp yazmadığınızı gerçekten umursamaz.
Max Langhof

1
Gerçek dünya performansının önbellek / ön getirme nedeniyle kesinlikle aynı olacağı garanti edilmez. OP'nin buna karşı dikkatli olması makul.
Max Langhof

1

Sayısal kararlılık ile doğruluk demek istiyorsanız, evet, doğruluk sorunları ile sonuçlanabilir. En büyük / en küçük değerlerin oranına ve sonuçtaki doğruluk gereksinimlerinize bağlı olarak, bu bir sorun olabilir veya olmayabilir.

Yüksek hassasiyete sahip olmak istiyorsanız, Kahan toplamını düşünün - bu, hata telafisi için ekstra bir şamandıra kullanır. Aynı zamanda ikili bir toplam vardır .

Doğruluk ve zaman arasındaki dengenin ayrıntılı analizi için bu makaleye bakın .

C ++ 17 için GÜNCELLEME:

Diğer cevaplardan bazıları belirtiliyor std::accumulate. C ++ 17'den beri algoritmaların paralelleştirilmesine izin veren yürütme ilkeleri vardır.

Örneğin

#include <vector>
#include <execution>
#include <iostream>
#include <numeric>

int main()
{  
   std::vector<double> input{0.1, 0.9, 0.2, 0.8, 0.3, 0.7, 0.4, 0.6, 0.5};

   double reduceResult = std::reduce(std::execution::par, std::begin(input), std::end(input));

   std:: cout << "reduceResult " << reduceResult << '\n';
}

Bu, büyük veri kümelerinin belirsiz olmayan yuvarlama hataları pahasına daha hızlı toplanmasını sağlamalıdır (kullanıcının iş parçacığı bölümlemesini belirleyemeyeceğini varsayıyorum).

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.