İşte sinyal işleme / kontrol sistemlerinden şu anda üzerinde çalıştığım gerçek dünya örneği:
Topladığınız verileri temsil eden bir yapıya sahip olduğunuzu varsayalım:
struct Sample {
time_t time;
double value1;
double value2;
double value3;
};
Şimdi bunları bir vektöre doldurduğunuzu varsayalım:
std::vector<Sample> samples;
... fill the vector ...
Şimdi, bir dizi numune üzerinde değişkenlerden birinin bazı fonksiyonlarını (ortalamasını söyleyin) hesaplamak istediğinizi ve bu ortalama hesaplamayı bir fonksiyonda hesaba katmak istediğinizi varsayalım. İşaretçi-üye bunu kolaylaştırır:
double Mean(std::vector<Sample>::const_iterator begin,
std::vector<Sample>::const_iterator end,
double Sample::* var)
{
float mean = 0;
int samples = 0;
for(; begin != end; begin++) {
const Sample& s = *begin;
mean += s.*var;
samples++;
}
mean /= samples;
return mean;
}
...
double mean = Mean(samples.begin(), samples.end(), &Sample::value2);
Not Daha kısa şablon işlevi yaklaşımı için 2016/08/05 tarihinde düzenlendi
Ve elbette, herhangi bir ileri yineleyici ve kendisiyle toplamayı ve size_t'ye bölünmeyi destekleyen herhangi bir değer türü için bir ortalama hesaplamak için bunu şablonlayabilirsiniz:
template<typename Titer, typename S>
S mean(Titer begin, const Titer& end, S std::iterator_traits<Titer>::value_type::* var) {
using T = typename std::iterator_traits<Titer>::value_type;
S sum = 0;
size_t samples = 0;
for( ; begin != end ; ++begin ) {
const T& s = *begin;
sum += s.*var;
samples++;
}
return sum / samples;
}
struct Sample {
double x;
}
std::vector<Sample> samples { {1.0}, {2.0}, {3.0} };
double m = mean(samples.begin(), samples.end(), &Sample::x);
EDIT - Yukarıdaki kodun performans sonuçları vardır
Kısa süre önce keşfettiğim gibi, yukarıdaki kodun bazı ciddi performans etkileri olduğunu not etmelisiniz. Özet, bir zaman serisinde özet bir istatistik hesaplarsanız veya bir FFT vb. Hesaplıyorsanız, her değişkenin değerlerini sürekli olarak bellekte saklamanızdır. Aksi takdirde, seri üzerinden yineleme yapılması alınan her değer için önbellek kaybına neden olur.
Bu kodun performansını göz önünde bulundurun:
struct Sample {
float w, x, y, z;
};
std::vector<Sample> series = ...;
float sum = 0;
int samples = 0;
for(auto it = series.begin(); it != series.end(); it++) {
sum += *it.x;
samples++;
}
float mean = sum / samples;
Birçok mimaride, bir örneği Sample
önbellek satırını dolduracaktır. Bu nedenle, döngünün her yinelemesinde, bir örnek bellekten önbelleğe alınır. Önbellek satırından 4 bayt kullanılacak ve geri kalanı atılacak ve bir sonraki yineleme başka bir önbellek kaybına, bellek erişimine vb.
Bunu yapmak için çok daha iyi:
struct Samples {
std::vector<float> w, x, y, z;
};
Samples series = ...;
float sum = 0;
float samples = 0;
for(auto it = series.x.begin(); it != series.x.end(); it++) {
sum += *it;
samples++;
}
float mean = sum / samples;
Şimdi ilk x değeri bellekten yüklendiğinde, sonraki üç değer de önbelleğe yüklenir (uygun hizalama varsayalım), yani sonraki üç yineleme için yüklenen herhangi bir değere ihtiyacınız yoktur.
Yukarıdaki algoritma, örneğin SSE2 mimarileri üzerinde SIMD talimatları kullanılarak biraz daha geliştirilebilir. Ancak, değerlerin tümü bellekte bitişikse bunlar çok daha iyi çalışır ve dört örneği birlikte yüklemek için tek bir talimat kullanabilirsiniz (daha sonraki SSE sürümlerinde daha fazla).
YMMV - veri yapılarınızı algoritmanıza uyacak şekilde tasarlayın.