#include <QtCore/QCoreApplication> #include <vector> #include <iostream> #include <QTime> #include <QDebug> #include <QThread> class MemReadTestThread : public QThread { const unsigned char * test_memory_block; const size_t test_memory_size; const size_t repeat_count; const size_t buf_size; public: MemReadTestThread(const unsigned char * block, const size_t size, const size_t bufsize, const size_t repeat_cnt) : QThread(), test_memory_block(block), test_memory_size(size), repeat_count(repeat_cnt), buf_size(bufsize) { } void run() { setPriority(HighPriority); std::vector<unsigned char> buf(buf_size); for(size_t n = 0; n < repeat_count; n++) { size_t i = 0; while(i + buf_size < test_memory_size) { memcpy(&buf[0], &test_memory_block[i], buf_size); i += buf_size; } } } }; class MemWriteTestThread : public QThread { unsigned char * test_memory_block; const size_t test_memory_size; const size_t repeat_count; const size_t buf_size; public: MemWriteTestThread(unsigned char * block, const size_t size, const size_t bufsize, const size_t repeat_cnt) : QThread(), test_memory_block(block), test_memory_size(size), buf_size(bufsize), repeat_count(repeat_cnt) { } void run() { setPriority(HighPriority); std::vector<unsigned char> buf(buf_size); for(size_t n = 0; n < repeat_count; n++) { size_t i = 0; while(i + buf_size < test_memory_size) { memcpy(&test_memory_block[i], &buf[0], buf_size); i += buf_size; } } } }; int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); using namespace std; const size_t size = 768*1024*1024; const size_t bufsize = 32*1024; vector<unsigned char> bytes1(size), bytes2(size); //засекаем время QTime time; time.restart(); const size_t count = 10; //создаем и запускаем 2 потока чтения из памяти MemReadTestThread rt1(&bytes1[0], bytes1.size(), bufsize, count); MemReadTestThread rt2(&bytes2[0], bytes2.size(), bufsize, count); rt1.start(); rt2.start(); //ждем, пока оба не завершатся rt1.wait(); rt2.wait(); //узнаем время выполнения и считаем результат float secs = (float) time.elapsed()/1000; size_t total_megabytes_copied = ((bytes1.size()+bytes2.size())/(1024*1024))*count; qDebug() << "********** memory read ************"; qDebug() << "time = " << secs << " sec, copied size = " << total_megabytes_copied << " megabytes"; float megbytes_per_sec = (float)total_megabytes_copied/secs; qDebug() << megbytes_per_sec << " megabytes per sec"; //засекаем время time.restart(); //создаем и запускаем два потока записи в память MemWriteTestThread wt1(&bytes1[0], bytes1.size(), bufsize, count); MemWriteTestThread wt2(&bytes2[0], bytes2.size(), bufsize, count); wt1.start(); wt2.start(); //ждем, пока не завершатся wt1.wait(); wt2.wait(); //узнаем время и вычисляем результат secs = (float) time.elapsed()/1000; total_megabytes_copied = ((bytes1.size()+bytes2.size())/(1024*1024))*count; qDebug() << "********** memory write ************"; qDebug() << "time = " << secs << " sec, copied size = " << total_megabytes_copied << " megabytes"; megbytes_per_sec = (float)total_megabytes_copied/secs; qDebug() << megbytes_per_sec << " megabytes per sec"; return a.exec(); }
Комментировать особо нечего, кроме того, почему нам нужно тестировать в двух потоках. Тут все очень интересно. Как всем известно, в современных контроллерах памяти, если есть две одинаковые (по объему и скорости) планки памяти, то есть возможность использовать двухканальный режим памяти. То есть две 64-битные планки памяти (а они имеют именно такую разрядность) как бы превращаются в одну большую 128-битную планку памяти. Соответственно, скорость работы увеличивается в 2 раза. Этот режим называется dual ganged mode. А есть еще dual unganged mode - это тоже режим с удвоенной пропускной способностью, но контроллер памяти обращается к памяти не как к одной 128-битной планке, а как к двум планкам, каждая из которых имеет свою собственную независимую 64-битную шину. То есть можно в одну планку писать и одновременно что-то читать с другой планки памяти. При этом удвоенная скорость будет только в том случае, если тестировать две планки памяти независимо друг от друга, то есть в разных потоках. Чтобы убедиться в этом, можно эту програмку сделать однопоточной и увидеть воочию двукратное падение скорости. Разумеется, это относится только к unganged mode, т.к. ganged mode совершенно все равно, сколько потоков будет запущено - на результат это не повлияет. Трудность заключается в том, что нет уверенности, что один тестируемый массив полностью расположен в одной планке, а второй - полностью в другой планке и это может негативно повлиять на итоговый результат. С другой стороны, в реальных приложениях вряд ли кто-то будет пытаться расположить массивы данных так, чтобы они оптимальным образом были рассредоточены по двум планкам памяти, поэтому результаты работы данного простого бенчмарка, в принципе, ближе к реальности, чем в идеальном случае. А результаты у меня такие: