Про тестирование оперативной памяти
Вчера осуществил задуманное и накатал простенький тест памяти, который запускается прямо из под 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).
Как ты выделяешь память для теста ?
И как ты гарантируешь полное покрытие физ.памяти тестом ?