Welcome to TechNet Blogs Sign in | Join | Help

Почему "4" Тоже Магическое Число

К тому, что семь – число магическое, все вроде бы привыкли. А почему четыре?

А потому, что согласно “__fastcall” calling convention, первые четыре аргумента функции передаются через регистры, а не через stack. То есть вызов функции

int __fastcall fooArg4(int a1, int a2, int a3, int a4);

должен работать *непропорционально* быстрее, чем

int __fastcall fooArg5(int a1, int a2, int a3, int a4, int a5);

А как же тогда быть с утверждением о том, что только первые 2 аргумента передаются через регистры ECX и EDX?

Оставим пока без обсуждения "родной до боли" x86, где современные процессоры делают много интересных трюков (на уровне микро архитектуры) для преодоления проблемы малого количества доступных программе регистров, и архитектуру IA64 “Itanium”, где регистров и так хватает для передачи большого числа аргументов.

Согласно MSDN, число 4 действительно является "магическим" для архитектур x64 и ARM. То есть добавление пятого аргумента функции по идее должно приводить к *нелинейному* увеличению времени вызова.

Но что бы ни написали в документации, часто хочется убедиться и знать наверняка. То есть, попробовать и измерить. Давайте попробуем:

int __fastcall fooArg4(int a1, int a2, int a3, int a4)
{
    return a1 + a2 + a3 + a4;
}

int __fastcall fooArg5(int a1, int a2, int a3, int a4, int a5)
{
    return a1 + a2 + a3 + a4 + a5;
}

int a1 = 1; int a2 = 2; int a3 = 3; int a4 = 4; int a5 = 5;

extern "C" void ExecuteMainBlock()
{
    int sum  = fooArg4(a1, a2, a3, a4);
        sum += fooArg5(a1, a2, a3, a4, a5);

    printf("sum: %d\n", sum);
}

int APIENTRY _tWinMain(HINSTANCE, HINSTANCE, LPTSTR, int)
{
    ExecuteMainBlock();
    return 0;
}

А теперь внимание, Вопрос: Как точно измерить количество циклов процессора (CPU core cycles), затраченных на выполнение соответствующих вызовов?

Опережая неотвратимую дискуссию о том, как правильно использовать и интерпретировать показания различных таймеров высокого разрешения (High Resolution Timers), позвольте сразу предложить правильный ответ: А НИКАК не измерить!

Потому что измерять скорость исполнения Debug Build с выключенной оптимизацией очевидно не имеет никакого смысла, а измерять Release Build этого примера c полной оптимизацией тоже не имеет смысла, что совсем не так уж очевидно. Почему? А потому, что оптимизирующий кодогенератор выдаст для предложенного примера следующий результат:

|WinMain| PROC
        str   lr, [sp, #-4]!

        ldr   r0, [pc, #0xC]
        mov   r1, #0x19
        bl    printf

        mov   r0, #0
        ldr   pc, [sp], #4
        ENDP

Что в переводе с "языка" ARM означает

int APIENTRY _tWinMain(HINSTANCE, HINSTANCE, LPTSTR, int)
{
    printf("sum: %d\n", 25);
    return 0;
}

Так что же делать? Как нам изменить предложенный пример, чтобы заставить компилятор построить оптимизированный код для вызова интересующих нас функций?

Предложения в студию, please! Если я опять не напортачил с конфигурированием блоггера, то канал комментариев должен быть открыт для всех.

Подсказка: предполагается, что обсуждение этого вопроса должно плавно перейти в обсуждение разных "интересностей" из Link Time Code Generation (LTCG).

Published Saturday, May 27, 2006 7:30 PM by Konstantin Isakov

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

# re: Почему "4" Тоже Магическое Число

Tuesday, June 20, 2006 2:43 AM by oleg t r
Здравствуйте!
Предлагаю вынести fooArg4 и fooArg5 в другой файл и отключить LTCG ;)

Leave a Comment

(required) 
required 
(required) 

  
Enter Code Here: Required
 
Page view tracker