Про тестирование оперативной памяти
Вчера осуществил задуманное и накатал простенький тест памяти, который запускается прямо из под Windows.
В качестве подопытного кролика была планка на 16GB, с которой возникла заминка (см. мой комментарий за 2017-10-20).
Как положено, сначала была простейшая однопоточная реализация, которая гоняла в цикле код вида:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | 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.
Прочитав его философию тестирования памяти, понял как можно обмануть кэши процессора.
Оказывается это не так и сложно. Достаточно заполнять буфер не последовательно за один проход, а с небольшим шагом и за несколько проходов:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | 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).
Как ты выделяешь память для теста ?
И как ты гарантируешь полное покрытие физ.памяти тестом ?