2013-07-16 3 views
14

Учитывая следующие тестовые программы:Почему разыменование делает мою программу быстрее?

Loop value on the stack

int main(void) { 
    int iterations = 1000000000; 

    while (iterations > 0) 
     -- iterations; 
} 

Loop value on the stack (dereferenced)

int main(void) { 
    int iterations = 1000000000; 
    int * p = & iterations; 

    while (* p > 0) 
     -- * p; 
} 

Loop value on the heap

#include <stdlib.h> 

int main(void) { 
    int * p = malloc(sizeof(int)); 
    * p = 1000000000; 

    while (*p > 0) 
     -- * p; 
} 

путем составления их -O0, я получаю следующее экс ecution раз:

case1.c 
real 0m2.698s 
user 0m2.690s 
sys  0m0.003s 

case2.c 
real 0m2.574s 
user 0m2.567s 
sys  0m0.000s 

case3.c 
real 0m2.566s 
user 0m2.560s 
sys  0m0.000s 

[править] Ниже среднее по 10 казней:

case1.c 
2.70364 

case2.c 
2.57091 

case3.c 
2.57000 

Почему время выполнения больше с первым тестом, который кажется самым простым?

Моя текущая архитектура - это виртуальная машина x86 (Archlinux). Я получаю эти результаты как с gcc (4.8.0), так и с clang (3.3).

[edit 1] Созданные коды ассемблера почти идентичны, за исключением того, что второй и третий имеют больше инструкций, чем первый.

[edit 2] Эти характеристики воспроизводимы (в моей системе). Каждое исполнение будет иметь тот же порядок величины.

[edit 3] Мне действительно не нравятся выступления неоптимизированной программы, но я не понимаю, почему это было бы медленнее, и мне любопытно.

+9

Попробуйте просмотреть сгенерированный код? Почему вы все равно заботитесь о производительности неоптимизированного кода? –

+8

Хе вы пытались запустить их в другом порядке? Являются ли эти однократные или средние значения значительным числом прогонов? – EJP

+0

@CarlNorum Почти такой же сгенерированный код, за исключением того, что в последних двух примерах есть больше инструкций (move & load). Меня не волнуют представления, но мне все еще любопытно :) –

ответ

6

Трудно сказать, является ли это причиной, так как я делаю некоторые догадки, и вы не указали некоторые особенности (например, какую цель вы используете). Но то, что я вижу, когда я компилирую без optimziations с целью x86 является следующие последовательности для decrementign в iterations переменную:

Случай 1:

L3: 
    sub DWORD PTR [esp+12], 1 
L2: 
    cmp DWORD PTR [esp+12], 0 
    jg L3 

Случай 2:

L3: 
    mov eax, DWORD PTR [esp+12] 
    mov eax, DWORD PTR [eax] 
    lea edx, [eax-1] 
    mov eax, DWORD PTR [esp+12] 
    mov DWORD PTR [eax], edx 
L2: 
    mov eax, DWORD PTR [esp+12] 
    mov eax, DWORD PTR [eax] 
    test eax, eax 
    jg L3 

одна большая разница что вы видите в случае 1, что инструкция на L3 считывает и записывает местоположение памяти. За ней сразу следует инструкция, которая считывает то же самое место памяти, которое было написано. Такая последовательность инструкций (то же самое место памяти, записанное, а затем сразу используемое в следующей инструкции) часто вызывает какой-то конвейерный останов в современных процессорах.

Вы заметите, что запись сразу за чтение одного и того же места нет в случае 2.

Опять же - этот ответ немного информированного спекуляции.

+0

Испытали ли вы свое, потому что ваш код ASM отличается от OP – aaronman

+0

OP, похоже, тот, что у Clang. Я получил этот же asm с gcc 4.8. – snf

+2

@ с истиной сомневаюсь, что этот вопрос будет иметь достаточно интересный ответ, чтобы кто-нибудь был удовлетворен – aaronman

Смежные вопросы