Жажда скорости Крис Касперски Хакер, номер #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% машинного времени! Количество протоков и продолжительность выполнения цикла подбираются экспериментально и для каждого приложения индивидуальны (а это значит, что для достижения наивысшей производительности придется отслеживать, какие приложения запущены, и выбирать соответствующий им профиль). Муторно, конечно, но разгон того стоит: Цикл, отбирающий процессорные такты у приложения, напрягающего кэш, и дающий ему время остыть |