PDA

Просмотр полной версии : Определяем частоту процессора, марку и модель.


Ivan_32
25.03.2009, 04:14
Немного теории:
Многие программист С++/Delphi часто задавались вопросом определения частоты процессор, это может понадобится например для выскоточных пауз независимых от частоты процессора(для того и нужно определить частоту что б сделать нужно количество команд, которое будет выполнятся на всех процессорах одинаково количество времени).
У процессора много разных интересных железных штук есть и вот одна из них это TSC - TimeStampCounter. Счетчик сумарно выполненых тактов с последнего ресета процессора. Он представляет собой Квад-слово или 8 байт. Внимание:Нижние четыре байта - прямой источник псевдо-случайных чисел в большинстве алгоритмов рандома! Прочесть значение этих 8 байт можно командой RDTSC. У этой команды нет ни аргументо и даже ложить в регистры ничего не надо, перед вызовом. Вызываем и в регстрах EDX:EAX(Верхние 4 байта в EDX, нижние 4 байта в EAX) оказывается текущее значение TSC.
На заметку: счетчик этот так же позволяет посчитать сумарное количество секунд аптайма компьютера. Иными словами - сколько он находился без перезагрузки. TSC/Частота проца = время аптайма.

Пример реализации:
rdtsc ;Считываем текущее значение TSC.
mov [timerLo],eax ;Помещаем текущее значение TSC
mov [timerHi],edx ;в переменные.
invoke Sleep,100 ;Ждем 100 милисекунд.
rdtsc ;Опять считываем уже новое значение TSC.
sub eax,[timerLo] ;Вычитаем из него предыдущее.
sbb edx,[timerHi] ;значение.
mov ecx,10 ;интервал 100 милисекунд - надо умножить
mul ecx ;на 10 что бы получить сколько происходит
; тактов за секунду. 1000 милисек = 1 сек.
timerLo и timerHi это DWORD-ы. Синтаксис FASM.

Вот таким простым образом мы узнали частоту процессора. Погрешности конечно могут быть и даже будут, но они несущественные.

Как ни странно но определить марку и модель процессора еще проще чем частоту. Всю информацию нам предоставит команда CPUID.
В EAX нужно поместить код нужных данных(все они вшиты в ROM процессора.). Нас интересует функция 80000002h 80000003h и 80000004h. Эта команда возвращает данные во все регистры общего назначения - EAX EBX ECX EDX, причем именно в такой последовательности.

Пример реализации:
mov eax,80000002h
cpuid
mov dword[brand+0],eax
mov dword[brand+4],ebx
mov dword[brand+8],ecx
mov dword[brand+12],edx
mov eax,80000003h
cpuid
mov dword[brand+16],eax
mov dword[brand+20],ebx
mov dword[brand+24],ecx
mov dword[brand+28],edx
mov eax,80000004h
cpuid
mov dword[brand+32],eax
mov dword[brand+36],ebx
mov dword[brand+40],ecx
mov dword[brand+44],edx

brand - это массив байт заданный вот так:
brand db 80 dup(0)

Все три функции(80000002h , 80000003h и 80000004h) возвращают разные части одной общей строки в которой содержится основная информация о процессоре. Для примера вот мое тестовое приложение:
[Ссылки могут видеть только зарегистрированные и активированные пользователи]
Приложу весь проект:
Dump.ru ([Ссылки могут видеть только зарегистрированные и активированные пользователи])

Ivan_32
23.09.2009, 09:47
Пока не забыл напишу - это на самом деле не самый точный способ и он совсем не подходит в некоторых случаях :) Например когда частота проца более 4.2 Ггц, потому все умные люди задействуют в этом вычислении FPU.

timer dq 0.0
temp dq 0.0


rdtsc
mov dword[timer],edx
mov dword[timer+4],eax
fild qword [timer]
fstp qword[timer] ; Самый простой способ конвертации из обычного числа в IEEE754
invoke Sleep(100)
mov dword[temp],edx
mov dword[temp],eax ; Как ни крути а грузить переменные в FPU можно только из памяти, обломно ниче не скажешь...
; Правда можно их просто в стек пихнуть на время, а потом его выровнять как все закончится:)
fild qword[temp]
fsub qword[timer]
push dword 10.0 ; немного уличной магии=)
fmul dword[esp]
fistp qword[timer]

Конечно размерность вывода значения не имеет - dword qword - неважно, главное что бы значение вместилось в него. Кстати, для тех кто не знает как можно работать с 64-битными целыми числами в 32-битном защищенном режиме - MMX.
Он конечно запилит FPU на время вычислений и запилит весь его стек скорее всего, но зато он умеет делать и SIMD вычисления, так что сей алгоритм можно и оптимизировать, тем более сейчас уже есть SSE - от FPU он не зависит, в отличие от MMX который просто использует 64-битные части FPU-регистров. Ну а для людей ленивых(вроде меня:)) я бы порекомендовал использовать функции семейства sprintf из MSVCRT.DLL или CRTDLL.DLL. Они там почти аналогичные - главное ни в коем случае не юзайте для этих целей NTDLL.DLL - С-функции в ней сугубо корявые и совсем не винрарные=)

invoke sprintf,szBuff,"%i64",dword[timer],dword[timer+4]