সিগন্যাল প্রসেসিং / নিয়ন্ত্রণ সিস্টেমগুলি থেকে আমি এখনই কাজ করছি এমন একটি বাস্তব-বিশ্বের উদাহরণ:
মনে করুন আপনার কাছে এমন কিছু কাঠামো রয়েছে যা আপনি যে ডেটা সংগ্রহ করছেন তা উপস্থাপন করে:
struct Sample {
time_t time;
double value1;
double value2;
double value3;
};
এখন ধরুন যে আপনি সেগুলি ভেক্টর হিসাবে স্টাফ করুন:
std::vector<Sample> samples;
... fill the vector ...
এখন ধরুন যে আপনি বিভিন্ন ধরণের নমুনার উপরের একটি ভেরিয়েবলের কিছু ফাংশন (গড় বলতে) বলছেন এবং আপনি এই গড় গণনাটিকে একটি ফাংশনে ফ্যাক্ট করতে চান। পয়েন্টার-থেকে-সদস্য এটি সহজ করে তোলে:
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);
দ্রষ্টব্য আরও সংক্ষিপ্ত টেম্পলেট-ফাংশন পদ্ধতির জন্য 2016/08/05 সম্পাদিত Note
এবং অবশ্যই, আপনি যেকোন ফরোয়ার্ড-ইটারেটর এবং যে কোনও মান ধরণের যা নিজের সাথে সংযোজন এবং আকার_টি দ্বারা বিভাগকে সমর্থন করে তার কোনও গড় গণনা করতে এটি টেমপ্লেট করতে পারেন:
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);
সম্পাদনা - উপরের কোডটিতে পারফরম্যান্সের প্রভাব রয়েছে
আপনার লক্ষ্য করা উচিত, আমি শীঘ্রই আবিষ্কার করেছি যে উপরের কোডটিতে কিছু মারাত্মক কর্মক্ষমতা রয়েছে lic সংক্ষিপ্তসারটি হ'ল আপনি যদি কোনও সময়ের সিরিজের একটি সংক্ষিপ্ত পরিসংখ্যান গণনা করছেন, বা কোনও FFT ইত্যাদি গণনা করছেন, তবে আপনার প্রতিটি ভেরিয়েবলের মানগুলি স্মৃতিতে স্বচ্ছলভাবে সংরক্ষণ করতে হবে। অন্যথায়, সিরিজটি পুনরাবৃত্তি প্রতিটি মান পুনরুদ্ধার জন্য একটি ক্যাশে মিস কারণ হতে পারে।
এই কোডটির কার্যকারিতা বিবেচনা করুন:
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;
অনেক স্থাপত্যে, এর একটি উদাহরণ Sample
ক্যাশে লাইন পূরণ করবে। লুপের প্রতিটি পুনরাবৃত্তির উপর, একটি নমুনা স্মৃতি থেকে ক্যাশে টানা হবে। ক্যাশে লাইন থেকে 4 বাইট ব্যবহার করা হবে এবং বাকীটি ফেলে দেওয়া হবে এবং পরবর্তী পুনরাবৃত্তির ফলস্বরূপ অন্য ক্যাশে মিস, মেমরি অ্যাক্সেস এবং এর ফলে দেখা যাবে।
এটি করা আরও ভাল:
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;
এখন যখন প্রথম এক্স মানটি মেমরি থেকে লোড করা হয়, পরবর্তী তিনটিও ক্যাশে লোড করা হবে (উপযুক্ত প্রান্তিককরণটি মনে করুন), পরবর্তী তিনটি পুনরাবৃত্তির জন্য আপনার কোনও মান বোঝানো হবে না।
উপরের অ্যালগরিদম যেমন SSE2 আর্কিটেকচারের সিমডি নির্দেশাবলী ব্যবহারের মাধ্যমে কিছুটা আরও উন্নত করা যেতে পারে। যাইহোক, এই কাজ অনেক কল্যাণকর, যদি মান মেমরি সব সংলগ্ন এবং আপনি (পরে সঙ্গে SSE সংস্করণে আরো) চার নমুনা একসঙ্গে লোড করতে একটি একক নির্দেশ ব্যবহার করতে পারেন।
ওয়াইএমএমভি - আপনার অ্যালগোরিদম অনুসারে আপনার ডেটা স্ট্রাকচার ডিজাইন করুন।