आपके काउंटर को बढ़ाने या घटाने से अधिक मायने रखता है कि क्या आप मेमोरी या डाउन मेमोरी में जा रहे हैं। अधिकांश कैश को मेमोरी में जाने के लिए ऑप्टिमाइज़ किया जाता है, मेमोरी को डाउन करने के लिए नहीं। चूँकि मेमोरी एक्सेस टाइम वह अड़चन है जिसका अधिकांश कार्यक्रम आज सामना करते हैं, इसका मतलब यह है कि आपके प्रोग्राम को बदलने से आप मेमोरी को बढ़ा सकते हैं, भले ही इसके लिए आपको अपने काउंटर की गैर-शून्य मान से तुलना करने की आवश्यकता हो, फिर भी प्रदर्शन में वृद्धि हो सकती है। अपने कुछ कार्यक्रमों में, मैंने अपने कोड को नीचे की बजाय मेमोरी में बदलने के लिए प्रदर्शन में महत्वपूर्ण सुधार देखा।
उलझन में? बस समय-समय पर लूप्स को ऊपर / नीचे मेमोरी में जाने का प्रोग्राम लिखें। यहाँ आउटपुट है जो मुझे मिला है:
Average Up Memory = 4839 mus
Average Down Memory = 5552 mus
Average Up Memory = 18638 mus
Average Down Memory = 19053 mus
(जहाँ "मस्क" का अर्थ माइक्रोसेकंड है) इस कार्यक्रम को चलाने से:
#include <chrono>
#include <iostream>
#include <random>
#include <vector>
//Sum all numbers going up memory.
template<class Iterator, class T>
inline void sum_abs_up(Iterator first, Iterator one_past_last, T &total) {
T sum = 0;
auto it = first;
do {
sum += *it;
it++;
} while (it != one_past_last);
total += sum;
}
//Sum all numbers going down memory.
template<class Iterator, class T>
inline void sum_abs_down(Iterator first, Iterator one_past_last, T &total) {
T sum = 0;
auto it = one_past_last;
do {
it--;
sum += *it;
} while (it != first);
total += sum;
}
//Time how long it takes to make num_repititions identical calls to sum_abs_down().
//We will divide this time by num_repitions to get the average time.
template<class T>
std::chrono::nanoseconds TimeDown(std::vector<T> &vec, const std::vector<T> &vec_original,
std::size_t num_repititions, T &running_sum) {
std::chrono::nanoseconds total{0};
for (std::size_t i = 0; i < num_repititions; i++) {
auto start_time = std::chrono::high_resolution_clock::now();
sum_abs_down(vec.begin(), vec.end(), running_sum);
total += std::chrono::high_resolution_clock::now() - start_time;
vec = vec_original;
}
return total;
}
template<class T>
std::chrono::nanoseconds TimeUp(std::vector<T> &vec, const std::vector<T> &vec_original,
std::size_t num_repititions, T &running_sum) {
std::chrono::nanoseconds total{0};
for (std::size_t i = 0; i < num_repititions; i++) {
auto start_time = std::chrono::high_resolution_clock::now();
sum_abs_up(vec.begin(), vec.end(), running_sum);
total += std::chrono::high_resolution_clock::now() - start_time;
vec = vec_original;
}
return total;
}
template<class Iterator, typename T>
void FillWithRandomNumbers(Iterator start, Iterator one_past_end, T a, T b) {
std::random_device rnd_device;
std::mt19937 generator(rnd_device());
std::uniform_int_distribution<T> dist(a, b);
for (auto it = start; it != one_past_end; it++)
*it = dist(generator);
return ;
}
template<class Iterator>
void FillWithRandomNumbers(Iterator start, Iterator one_past_end, double a, double b) {
std::random_device rnd_device;
std::mt19937_64 generator(rnd_device());
std::uniform_real_distribution<double> dist(a, b);
for (auto it = start; it != one_past_end; it++)
*it = dist(generator);
return ;
}
template<class ValueType>
void TimeFunctions(std::size_t num_repititions, std::size_t vec_size = (1u << 24)) {
auto lower = std::numeric_limits<ValueType>::min();
auto upper = std::numeric_limits<ValueType>::max();
std::vector<ValueType> vec(vec_size);
FillWithRandomNumbers(vec.begin(), vec.end(), lower, upper);
const auto vec_original = vec;
ValueType sum_up = 0, sum_down = 0;
auto time_up = TimeUp(vec, vec_original, num_repititions, sum_up).count();
auto time_down = TimeDown(vec, vec_original, num_repititions, sum_down).count();
std::cout << "Average Up Memory = " << time_up/(num_repititions * 1000) << " mus\n";
std::cout << "Average Down Memory = " << time_down/(num_repititions * 1000) << " mus"
<< std::endl;
return ;
}
int main() {
std::size_t num_repititions = 1 << 10;
TimeFunctions<int>(num_repititions);
std::cout << '\n';
TimeFunctions<double>(num_repititions);
return 0;
}
दोनों sum_abs_up
और sum_abs_down
एक ही काम करते हैं (संख्याओं के सदिश राशि) और केवल अंतर के साथ उसी तरह से समयबद्ध होते हैं जो sum_abs_up
स्मृति जाते समय स्मृति के ऊपर sum_abs_down
जाती है। मैं vec
संदर्भ से भी गुजरता हूं ताकि दोनों फ़ंक्शन समान मेमोरी स्थानों तक पहुंच सकें। फिर भी, sum_abs_up
लगातार तेजी से है sum_abs_down
। इसे स्वयं चलाएं (मैंने इसे g ++ -O3 के साथ संकलित किया)।
यह नोट करना महत्वपूर्ण है कि मैं कितना लूप टाइट कर रहा हूं। यदि एक लूप का शरीर बड़ा है, तो यह संभवत: कोई फर्क नहीं पड़ता कि इसका पुनरावृत्ति मेमोरी में ऊपर या नीचे जाता है क्योंकि लूप के शरीर को निष्पादित करने में लगने वाला समय पूरी तरह से हावी होगा। इसके अलावा, यह उल्लेख करना महत्वपूर्ण है कि कुछ दुर्लभ छोरों के साथ, स्मृति को नीचे जाना कभी-कभी इसे ऊपर जाने से तेज होता है। लेकिन इस तरह के छोरों के साथ भी ऐसा कभी नहीं हुआ था कि स्मृति हमेशा ऊपर जाती थी की तुलना धीमी होती थी (छोटे शरीर वाले छोरों के विपरीत, जो मेमोरी में ऊपर जाती हैं, जिसके लिए विपरीत अक्सर सही होता है; वास्तव में, एक छोटे से मुट्ठी भर छोरों के लिए) समय सीमा समाप्त हो गई, मेमोरी बढ़ने से प्रदर्शन में 40% की वृद्धि हुई)।
बिंदु, अंगूठे के एक नियम के रूप में, यदि आपके पास विकल्प है, अगर लूप का शरीर छोटा है, और यदि आपके लूप के नीचे जाने के बजाय इसकी मेमोरी में थोड़ा अंतर है, तो आपको मेमोरी को ऊपर ले जाना चाहिए।
एफवाईआई vec_original
प्रयोग के लिए है, इसे बदलने में आसान बनाने के लिए sum_abs_up
और भविष्य में समय को प्रभावित करने के लिए इन परिवर्तनों को अनुमति नहीं देते हुए sum_abs_down
उन्हें बदल vec
देता है। मैं अत्यधिक के साथ प्रयोग करना की सलाह देते हैं sum_abs_up
और sum_abs_down
और परिणाम समय।