Мы профилируем сложную программу на C ++, которая выполняет несколько итераций алгоритма. Время имеет решающее значение, и мы хотим минимизировать время выполнения каждой итерации. Алгоритм таков, что время выполнения должно быть очень похожим для каждой итерации, но мы обнаруживаем, что время выполнения уменьшается с последовательными итерациями. Мы подозреваем, что за это отвечает кеш, но не можем полностью объяснить, что мы видим, основываясь на нашем понимании кешей.
Мы выполняем код на процессоре Intel Xeon с Centos 7.6, скомпилированным g ++ 7.3. 1.
Нам удалось продемонстрировать поведение, используя простую программу, показанную ниже:
#include <vector>
#include <fstream>
#include <array>
#include <chrono>
#include <iostream>
int main()
{
std::chrono::high_resolution_clock::time_point t1;
std::chrono::high_resolution_clock::time_point t2;
const unsigned NUM_BUFFERS = 200;
const unsigned BUFFER_SIZE_BYTES = 1024 * 1024;
const unsigned NUM_TRIALS = 50;
std::vector<uint8_t*> buffers;
for (int buff=0; buff<NUM_BUFFERS; ++buff)
buffers.push_back(new uint8_t[BUFFER_SIZE_BYTES]);
std::vector<double> tAll; // Records execution time for each buffer write
tAll.resize(NUM_TRIALS*NUM_BUFFERS);
unsigned indt = 0;
// For each trial
for ( unsigned indTrial=0; indTrial<NUM_TRIALS; indTrial++ )
{
// For all buffers
for ( unsigned indBuffer=0; indBuffer<NUM_BUFFERS; indBuffer++ )
{
t1 = std::chrono::high_resolution_clock::now();
// Increment contents of entire buffer
uint8_t* p_buff = buffers.at(indBuffer);
for ( unsigned ind=0; ind<BUFFER_SIZE_BYTES; ind++ )
{
p_buff[ind]++;
}
t2 = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> duration = std::chrono::duration_cast<std::chrono::duration<double>>(t2 - t1);
tAll.at(indt++) = duration.count();
}
}
// Write execution times to a file
std::ofstream fp;
fp.open("TEST_ARTEMIS.TXT");
double max=0;
for ( unsigned ind=0; ind<tAll.size(); ind++ )
{
fp << tAll[ind] << std::endl;
}
}
Эта программа увеличивает каждый байт серии из 200 из 1 МБ буферов. Процесс повторяется 50 раз .
Время для каждой полной записи каждого буфера записывается в файл. Если мы построим эти времена и увеличим масштаб до первых 250 операций записи в буфер, мы увидим:
![enter image description here](https://i.stack.imgur.com/hyteK.png)
Первая запись в буфер занимает ~ 10 мс, следующая немногие берут ~ 3 мс, следующие 200 берут ~ 2,5 мс, а затем время падает до 2 мс.
Мы не думаем, что это поведение можно объяснить простым поведением кеша, поскольку кеши L2 / L3 не являются достаточно большой, чтобы вместить все буферы, поэтому записи в кеш должны происходить на протяжении всего эксперимента. Как будто память «нагревается» и ускоряется со временем.
Может кто-нибудь предложить объяснение того, что мы видим, пожалуйста?