与要增加还是减少计数器相比,重要的是要增加内存还是减少内存。大多数缓存都针对增加内存而不是减少内存进行了优化。由于内存访问时间是当今大多数程序面临的瓶颈,因此,即使需要将计数器与非零值进行比较,更改程序以提高内存容量也可以提高性能。在某些程序中,通过更改代码以增加内存而不是减少内存,我看到了性能上的显着改善。
怀疑吗?只需编写一个程序来增加/减少内存的时间循环。这是我得到的输出:
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%以上。
根据经验,要点是,如果可以选择,如果循环的主体很小,并且让循环增加内存而不是减少内存之间没有什么区别,则应该增加内存。
FYI vec_original
在这里进行试验,以使其易于更改,sum_abs_up
并sum_abs_down
使其进行更改,vec
同时不允许这些更改影响将来的时间。我高度推荐玩弄sum_abs_up
与sum_abs_down
和计时的结果。