Издательский дом ООО "Гейм Лэнд"ЖУРНАЛ ХАКЕР #95, НОЯБРЬ 2006 г.

Жажда скорости

Крис Касперски

Хакер, номер #095, стр. 095-130-6


Процедура закрытия драйвера должна просто вызывать CloseHandle, а все остальное за нас сделает сам драйвер. Впрочем, драйвер можно и не закрывать. При выходе из приложения операционная система сделает это автоматически.

Прикладная функция, выгружающая драйвер из памяти

int _996_exit()

{

if (_996_handle != INVALID_HANDLE_VALUE)

{

CloseHandle(_996_handle);

}

return 1;

}

А вот при снятии показаний со счетчиков производительности без ассемблера уже не обойтись! Для упрощения программирования можно использовать ассемблерные вставки, хоть это и является признаком дурного тона, так как затрудняет перенос программы на другие платформы и препятствует ее компиляции другим компилятором. Правильным решением было бы создание отдельного ассемблерного модуля, но это слишком хлопотно, тем более что мы пишем не коммерческую программу, а всего лишь демонстрационный макет.

// ИНИЦИЛИЗАЦИЯ ДРАЙВЕРА 996

if (_996_init()==0) return printf("-ERR: 996 driver not loaded!\n");

// ВЫБОР СОБЫТИЯ ДЛЯ МОНИТОРИНГА И ЗАПУСК СЧЕТЧИКА

_996_select(DCU_MISS_OUTSTANDING, PrefCtrl0);

for(;;)

{

__asm

{

mov ecx, PrefCtrl0 ; // читаем регистр PrefCtrl0...

RDPMC ; // ...и помещаем результат в EDX:EAX

mov _edx, edx ; // сохраняем EDX:EAX в...

mov _eax, eax ; // ...одноименных переменных

}

// анализ кол-ва кэш-промахов

// ===========================

…

Sleep(0); ; // отдаем остаток кванта и спим

}

При снятии показания со счетчиков производительности следует учитывать, что они возвращают количество кэш-промахов с момента запуска счетчика, а не между двумя соседними замерами, так что дельту придется считать самостоятельно. И если эта дельта вдруг превысит некоторое пороговое значение (задаваемое настройками нашей программы), необходимо «притормозить» процессор, чтобы кэш чуть-чуть приостыл. А как это можно сделать? Ведь даже если материнская плата поддерживает изменение тактовой частоты процессора на лету, каждая из них делает это по-разному, и у нас получается громоздкая и не универсальная программа.

На самом деле, нет ничего проще! Достаточно просто прекратить отдавать кванты, загрузив процессор «тупой» работой, не требующей обращения к памяти. Например, складывать два регистра в цикле. При условии, что в системе имеются два активных потока, один из которых принадлежит приложению, гоняющему кэш и в хвост, и в гриву, а другой — гоняет цикл в нашей программе, на однопроцессорных материях операционная система будет выделять приложению только 50% машинного времени, следовательно, нагрузка на кэш упадает. А если мы запустим три потока, мотающие такие циклы, кэш-приложение получит только 25% машинного времени! Количество протоков и продолжительность выполнения цикла подбираются экспериментально и для каждого приложения индивидуальны (а это значит, что для достижения наивысшей производительности придется отслеживать, какие приложения запущены, и выбирать соответствующий им профиль). Муторно, конечно, но разгон того стоит:

Цикл, отбирающий процессорные такты у приложения, напрягающего кэш, и дающий ему время остыть

Назад на стр. 095-130-5  Содержание  Вперед на стр. 095-130-7