Сколько инструкций получить доступ к указателю в C?

Я пытаюсь выяснить, сколько тактов или общих инструкций требуется для доступа к указателю в C. Я не думаю, что знаю, как выяснить, например, p-> x = d-> + f-> b

я принял бы две загрузки на указатель, просто предположив, что будет загрузка для указателя и загрузка для значения. Таким образом в этом операции, разрешение указателя было бы намного большим фактором, чем фактическое дополнение, до попытки ускорить этот код, правильно?

Это может зависеть от компилятора и реализованной архитектуры, но является мной на правильном пути?

Я видел некоторый код, где каждое значение, используемое в, говорит, 3 дополнения, прибыл из a

 f2->sum = p1->p2->p3->x + p1->p2->p3->a + p1->p2->p3->m

тип структуры, и я пытаюсь определить, как плохо это

5
задан James Morris 30 April 2010 в 20:46
поделиться

4 ответа

Это зависит от используемой архитектуры.

Некоторые архитектуры могут ссылаться на память / разыменовать память для инструкции без предварительной загрузки ее в регистр, другие - нет. Некоторые архитектуры не имеют понятия инструкций, которые вычисляют смещения для вас для разыменования и заставят вас загрузить адрес памяти, добавить к нему ваше смещение, а затем позволить вам разыменовать местоположение в памяти. Я уверен, что есть больше отклонений от чипа к чипу.

Как только вы их преодолеете, каждая инструкция также займет разное количество времени в зависимости от архитектуры. Честно говоря, это очень и очень минимальные накладные расходы.

Что касается вашего непосредственного вопроса о разыменовании цепочки элементов, то медлительность будет заключаться в том, что чем дальше вы продвигаетесь в цепочке разыменования, тем вероятнее будет плохая локализация ссылки. Это означает большее количество промахов в кэше, что означает большее количество обращений в основную память (или на диск!) Для получения данных. Основная память очень медленная по сравнению с процессором.

8
ответ дан 13 December 2019 в 05:32
поделиться

Зависит от того, что вы делаете, тривиальное разыменование указателя y = * z; где

int x = 1;
int* z = &x;
int y;

может собираться в нечто подобное на x86:

mov eax, [z]
mov eax, [eax]
mov [y], eax

и y = x все равно потребуют разыменования памяти:

mov eax, [x]
mov [y], eax

Инструкции перемещения в память занимают около 2-4 циклов IIRC.

Хотя, если вы загружаете память из совершенно случайных мест, вы будете вызывать множество ошибок страниц, что приведет к потере сотен тактовых циклов.

1
ответ дан 13 December 2019 в 05:32
поделиться

Там, где это возможно, компилятор удалит эти накладные расходы, сохраняя повторно используемые базовые местоположения в регистре (например, p1-> p2-> p3 в вашем примере).

Однако иногда компилятор не может определить, какие указатели могут быть псевдонимами других указателей, используемых в вашей функции - это означает, что он должен вернуться в очень консервативное положение и часто перезагружать значения из указателей.

Здесь может помочь ключевое слово C99 restrict . Он позволяет сообщать компилятору, когда определенные указатели никогда не имеют псевдонимов других указателей в области действия функции, что, следовательно, может улучшить оптимизацию.


Например, возьмем эту функцию:

struct xyz {
    int val1;
    int val2;
    int val3;
};

struct abc {
    struct xyz *p2;
};

int foo(struct abc *p1)
{
    int sum;

    sum = p1->p2->val1 + p1->p2->val2 + p1->p2->val3;

    return sum;
}

В gcc 4.3.2 с уровнем оптимизации -O1 он компилируется в этот код x86:

foo:
    pushl   %ebp
    movl    %esp, %ebp
    movl    8(%ebp), %eax
    movl    (%eax), %edx
    movl    4(%edx), %eax
    addl    (%edx), %eax
    addl    8(%edx), %eax
    popl    %ebp
    ret

Как вы можете видеть, он только учитывает ] p1 один раз - он сохраняет значение p1-> p2 в регистре % edx и использует его трижды для выборки трех значений из этой структуры.

1
ответ дан 13 December 2019 в 05:32
поделиться

Некоторые IDE, такие как VisualStudio, позволяют просматривать сгенерированную сборку вместе с исходным кодом.

Как просмотреть сборку, стоящую за кодом, с помощью Visual C ++?

Тогда вы сможете увидеть, как это выглядит для вашей точной архитектуры и реализации.

Если вы используете GDB (linux, mac), используйте disassemble

(gdb) disas 0x32c4 0x32e4
Dump of assembler code from 0x32c4 to 0x32e4:
0x32c4 <main+204>:      addil 0,dp
0x32c8 <main+208>:      ldw 0x22c(sr0,r1),r26
0x32cc <main+212>:      ldil 0x3000,r31
0x32d0 <main+216>:      ble 0x3f8(sr4,r31)
0x32d4 <main+220>:      ldo 0(r31),rp
0x32d8 <main+224>:      addil -0x800,dp
0x32dc <main+228>:      ldo 0x588(r1),r26
0x32e0 <main+232>:      ldil 0x3000,r31
End of assembler dump.
2
ответ дан 13 December 2019 в 05:32
поделиться
Другие вопросы по тегам:

Похожие вопросы: