比增加或减少计数器更重要的是增加内存还是减少内存。大多数缓存都针对增加内存而不是减少内存进行了优化。由于内存访问时间是当今大多数程序面临的瓶颈,这意味着更改程序以增加内存可以提高性能,即使这需要将计数器与非零值进行比较。在我的一些程序中,通过将代码更改为增加内存而不是减少内存,我看到了性能的显着提高。
怀疑?只需编写一个程序来增加/减少内存的时间循环。这是我得到的输出:
Average Up Memory = 4839 mus
Average Down Memory = 5552 mus
Average Up Memory = 18638 mus
Average Down Memory = 19053 mus
(其中“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 并为结果计时。