HLFX.Ru Forum
Показать все 4 сообщений этой темы на одной странице

HLFX.Ru Forum (https://hlfx.ru/forum/index.php)
- Half-Life SDK (https://hlfx.ru/forum/forumdisplay.php?forumid=8)
-- Замеряем скорость выполнения кода (https://hlfx.ru/forum/showthread.php?threadid=595)


Отправлено XaeroX 02-04-2007 в 17:55:

Замеряем скорость выполнения кода

Не совсем про ХЛ, но все же может быть полезно и моддерам.
В общем, понадобилось мне точно замерять, насколько та или иная реализация алгоритма быстрее. Есть конечно много вариантов, можно GetTickCount использовать или QueryPerformanceCounter, но тут есть две проблемы:
- Невысокая точность
- Сами эти функции содержат код
Это не так принципиально для профилирования больших функций, а если вам надо профилировать небольшие ассемблерные вставки?
Для этого я выбрал TSC - timestamp counter. Это модельно-зависимый регистр процессора, содержимое которого увеличивается с каждым тактом ядра и сбрасывается при остановке (сигналом RESET). Регистр этот 64-битный, так что и работать придется с 64-битными переменными (в MS Visual Studio это тип __int64). Есть тут две тонкости. Во-первых, нужно проверить, так как регистр модельно-зависимый, то по определению он присутствует не на всех моделях процессоров, так что нам нужно будет проверить его наличие. И второе - чтение из него выполняется командой RDTSC, которая может быть сделана защищенной (доступной только в т.н. "нулевом кольце" привилегий). Под WinXP вы такие привилегии не получите... Но обычно она все же не защищена, так что эту проверку я опускаю. Ну и на всякий случай проверим, что в регистре ненулевое значение (иначе - что-то пошло не так).
Я написал три функции - первые две служат для сравнения производительности двух участков кода, а вторая просто выводит число тактов за профилирование.

C++ Source Code:
1
unsigned __int64 __g_ProfilerStart;
2
unsigned __int64 __g_ProfilerEnd;
3
unsigned __int64 __g_ProfilerEnd2;
4
unsigned __int64 __g_ProfilerSpare;
5
unsigned __int8  __g_ProfilerSupported;
6
 
7
void Prof_Init( void )
8
{
9
  __g_ProfilerStart = 0;
10
  __g_ProfilerEnd = 0;
11
  __g_ProfilerEnd2 = 0;
12
  __g_ProfilerSpare = 0;
13
 
14
  __asm {
15
    //Check TSC support (bit 4 in edx, with eax=1)
16
    mov eax, 1
17
    cpuid
18
    test edx, 10h
19
    jz unsupported
20
 
21
    //Check TSC value, it must be non-zero
22
    rdtsc
23
    test eax, eax
24
    jnz supported
25
    test edx, edx
26
    jz unsupported
27
  }
28
supported:
29
  __g_ProfilerSupported = 1;
30
  return;
31
 
32
unsupported:
33
  __g_ProfilerSupported = 0;
34
  return;
35
}

Сами функции профилирования - это макросы, т.к. они должны вставляться строго в код (тратить такты на вызов функций недопустимо). Обратите внимание - во втором макросе RDTSC вызывается дважды. Это делается, чтобы скорректировать полученное значение на такты, затраченные на сами команды профилирования.
C++ Source Code:
1
#define Prof_Start()	\
2
{	\
3
if (__g_ProfilerSupported) \
4
{ \
5
__asm pushad \
6
__asm rdtsc \
7
__asm mov DWORD PTR[__g_ProfilerStart+4], edx \
8
__asm mov DWORD PTR[__g_ProfilerStart], eax \
9
__asm popad \
10
} \
11
}
12
 
13
#define Prof_End()	\
14
{	\
15
if (__g_ProfilerSupported) \
16
{ \
17
__asm pushad \
18
__asm rdtsc \
19
__asm mov DWORD PTR[__g_ProfilerEnd+4], edx \
20
__asm mov DWORD PTR[__g_ProfilerEnd], eax \
21
__asm popad \
22
__asm pushad \
23
__asm rdtsc \
24
__asm mov DWORD PTR[__g_ProfilerEnd2+4], edx \
25
__asm mov DWORD PTR[__g_ProfilerEnd2], eax \
26
__asm popad \
27
} \
28
}

Далее собственно обработка результатов. Первая функция вычисляет запоминает значение первого профилирования, вторая - вычисляет второе значение и выводит, какой код быстрее и на сколько процентов.
C++ Source Code:
1
void Prof_Store( void )
2
{
3
  if (__g_ProfilerSupported) {
4
    __g_ProfilerSpare = __g_ProfilerEnd-__g_ProfilerStart-(__g_ProfilerEnd2-__g_ProfilerEnd);
5
  }
6
}
7
 
8
void Prof_RatioResults( void )
9
{
10
  if (__g_ProfilerSupported) {
11
    unsigned __int64 total = __g_ProfilerEnd-__g_ProfilerStart-(__g_ProfilerEnd2-__g_ProfilerEnd);
12
    double ratio;
13
 
14
    if (total >= __g_ProfilerSpare) {
15
      ratio = (double)(total-__g_ProfilerSpare)/total;
16
      printf("First code is %.2f%% faster\n", ratio*100);
17
    } else {
18
      ratio = (double)(__g_ProfilerSpare-total)/__g_ProfilerSpare;
19
      printf("Second code is %.2f%% faster\n", ratio*100);
20
    }
21
  } else {
22
    printf("--- Profiler not supported ---\n");
23
    printf("Possible reasons:\n");
24
    printf("\t- Your CPU does not support timestamp counter\n");
25
    printf("\t- RDTSC is a priveleged instruction (only at CPL0)\n");
26
  }
27
}

Эта функция просто выводит результат профилирования:
C++ Source Code:
1
void Prof_Results( void )
2
{
3
  if (__g_ProfilerSupported) {
4
    unsigned __int64 total = __g_ProfilerEnd-__g_ProfilerStart-(__g_ProfilerEnd2-__g_ProfilerEnd);
5
    printf("--- Profile results: %I64d\n", total);
6
  } else {
7
    printf("--- Profiler not supported ---\n");
8
    printf("Possible reasons:\n");
9
    printf("\t- Your CPU does not support timestamp counter\n");
10
    printf("\t- RDTSC is a priveleged instruction (only at CPL0)\n");
11
  }
12
}


Вот и все. Профилировать можно так:
C++ Source Code:
1
Prof_Init();
2
Prof_Start();
3
 
4
//Code 1
5
 
6
Prof_End();
7
Prof_Store();
8
 
9
Prof_Start();
10
 
11
//Code 2
12
 
13
Prof_End();
14
Prof_RatioResults();


Учтите, что при профилировании длительно выполняемого кода неизбежны ошибки. Это следствие вытесняющей многозадачности ОС: она может прервать по собственному желанию выполнение вашего кода и переключить процессор на код ОС. Поэтому во время профилирования не сворачивайте окна, не двигайте мышь и т.п.
Ваше мнение о профайлере? Предложения и замечания приветствуются

__________________

xaerox on Vivino


Отправлено Government-Man 03-04-2007 в 07:50:

По поводу самого кода ничего сказать не могу - ибо в асме я разбираюсь мягко говоря плоховато. А за тутор спасибо - сам искал.


Отправлено Дядя Миша 03-04-2007 в 12:21:

Government-Man не переживай, ксерокс этот профайлер тоже не сразу написал, а после соответствующей подготовки.


Отправлено Government-Man 03-04-2007 в 14:32:

Цитата:
Дядя Миша писал:
после соответствующей подготовки


См. тему "Что вы нюхаете"


Временная зона GMT. Текущее время 11:41.
Показать все 4 сообщений этой темы на одной странице

На основе vBulletin версии 2.3.0
Авторское право © Jelsoft Enterprises Limited 2000 - 2002.
Дизайн и программирование: Crystice Softworks © 2005 - 2024