Про тестирование оперативной памяти
Вчера осуществил задуманное и накатал простенький тест памяти, который запускается прямо из под Windows.
В качестве подопытного кролика была планка на 16GB, с которой возникла заминка (см. мой комментарий за 2017-10-20).
Как положено, сначала была простейшая однопоточная реализация, которая гоняла в цикле код вида:
void MemTest_001(void* const pvBeg,void* const pvEnd)
{
//последовательное заполнение одним и тем же значением
typedef unsigned char value_type;
const size_t nValues=(((const char*)pvEnd)-((const char*)pvBeg))/sizeof(value_type);
value_type* const beg=reinterpret_cast<value_type*>(pvBeg);
value_type* const end=beg+nValues;
value_type v=0;
for(;;++v)
{
std::fill(beg,end,v);
for(const value_type* p=beg;p!=end;++p)
{
const value_type m=*p;
if(m==v)
continue;
structure::str_formatter
fmsg("bad value [%1]. expected value [%2]. position [%3].");
fmsg<<m<<v<<size_t(p-beg);
throw std::runtime_error(fmsg.str());
}//for
if(v==std::numeric_limits<value_type>::max())
break;
}//for v
}//MemTest_001
Запустил на 16GB и не дождался пока этот тест отработает до конца.
Распределил работу этого теста между десятью потоками (процессор 6950X). Отработало приблизительно минут за двенадцать — обвязка тестов убогая, поэтому время измерялось на глаз.
Если честно, я не ожидал, что все будет так медленно.
Потом я накатал несколько других тестов, которые последовательно заполняют память. Отрабатывают, но, чувствую — что-то все не то.
Полез в сеть, почитать про настоящий MemTest. Нашел его исходный код на github.
Прочитав его философию тестирования памяти, понял как можно обмануть кэши процессора.
Оказывается это не так и сложно. Достаточно заполнять буфер не последовательно за один проход, а с небольшим шагом и за несколько проходов:
void MemTest_002_modx(void* const pvBeg,void* const pvEnd)
{
typedef unsigned char value_type;
const size_t nValues=(((const char*)pvEnd)-((const char*)pvBeg))/sizeof(value_type);
value_type* const beg=reinterpret_cast<value_type*>(pvBeg);
value_type* const end=beg+nValues;
size_t c_step=37;
for(value_type s=0;;++s)
{
value_type v=s;
for(size_t iStep=0;iStep!=c_step;++iStep)
{
for(value_type* p=beg+iStep;p<end;p+=c_step,++v)
(*p)=v;
}
v=s;
for(size_t iStep=0;iStep!=c_step;++iStep)
{
for(value_type* p=beg+iStep;p<end;p+=c_step,++v)
{
const value_type m=*p;
if(m==v)
continue;
structure::str_formatter
fmsg("bad value [%1]. expected value [%2]. position [%3].");
fmsg<<m<<v<<size_t(p-beg);
throw std::runtime_error(fmsg.str());
}//for
}//for
if(s==std::numeric_limits<value_type>::max())
break;
}//for s
}//MemTest_002_modx
Запустив этот тест в 10 потоков, через пару часов я обнаружил, что он отработал только на 20%.
Пришлось оставить его на ночь.
За ночь он осилил первый проход и начал второй.
Судя по грубым прикидкам, такая работа с оперативной памяти в пятьдесят (ПЯТЬДЕСЯТ, Карл!) медленнее последовательной. ~10 часов против ~12 минут.
Хорошо так отрезвляет. Вспомнились две вещи.
Первая — Celeron 266.
Вторая — машина Тьюринга оптимизация работы с HDD. Намного быстрее сначала прочитать блок страниц, модифицировать нужные страницы, а потом записать блок назад. Это я узнал от LOA, Олега LOA 🙂 Как внезапно оказалось — с оперативной памятью нужно делать тоже самое.
hvlad on 21 октября, 2017
Memtest не зря работает до загрузки ОС (IIRC).
Как ты выделяешь память для теста ?
И как ты гарантируешь полное покрытие физ.памяти тестом ?