Разница в производительности при доступе с использованием указателя и двойного указателя

  1. Есть ли разница в производительности при доступе к ячейке памяти с помощью указателя и двойного указателя?
  2. Если так, какой из них быстрее?

4 ответа

Решение

Нет простого ответа, так как ответ может зависеть от конкретной машины. Если я правильно помню, некоторые устаревшие машины (например, PDP11) предлагали доступ с "двойным указателем" в одной инструкции.

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

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

Как примечание, я верю в реальные программы, разница незначительна (по сравнению с чем-либо еще, что делается в программе), и если только это не сделано в очень чувствительном к производительности цикле - просто делайте то, что более читабельно. Кроме того, компилятор может оптимизировать его для вас, если это возможно.

Предполагая, что вы говорите о чем-то вроде

int a = 10;
int *aptr = &a;
int **aptrptr = &aptr;

Тогда стоимость

*aptr = 20;

Это одна разыменование. Адрес, на который указывает aptr сначала должен быть получен, а затем адрес может быть сохранен в.

Цена

**aptrptr = 30;

Есть две разыменования. Адрес, на который указывает aptrptr сначала должен быть найден. Затем адрес, сохраненный в этом адресе, должен быть найден. Затем этот адрес может быть сохранен в.

Это то, что вы спрашивали?

Поэтому завершить использование одного указателя быстрее, если это соответствует вашим потребностям.

Обратите внимание, что если вы обращаетесь к указателю или двойному указателю в цикле, например,

while(some condition)
    *aptr = something;

или же

while(some condition)
    **aptrptr = something;

Компилятор, вероятно, оптимизирует так, чтобы разыменование выполнялось только один раз в начале цикла, поэтому стоимость составляет только 1 дополнительную выборку адреса, а не N, где N - это число, сколько раз выполняется цикл.

РЕДАКТИРОВАТЬ: (1) Как правильно Амит указывает, "как" доступа к указателю явно не вещь C... это зависит от базовой архитектуры. Если ваша машина поддерживает двойное разыменование как отдельную инструкцию, то, возможно, не будет большой разницы. В качестве примера он использует режим отложенной адресации индекса PDP11. Возможно, вы обнаружите, что такая инструкция все еще потребляет больше циклов... обратитесь к документации по аппаратному обеспечению и посмотрите на оптимизацию, которую ваш компилятор C может применить для вашей конкретной архитектуры.

Архитектура PDP11 примерно 1970-х годов. Насколько я знаю (если кто-то знает, что современная архитектура может сделать это после публикации!), Большинство архитектур RISC не имеют такой двойной разыменования и, вероятно, должны будут сделать две выборки, насколько мне известно.

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

Мне стало любопытно и я настроил следующий сценарий:

int v = 0;
int *pv = &v;
int **ppv = &pv;

Я попытался разыменовать указатели и взглянул на разборку, которая показала следующее:

int x;
x = *pv;
    00B33C5B  mov         eax,dword ptr [pv]  
    00B33C5E  mov         ecx,dword ptr [eax]  
    00B33C60  mov         dword ptr [x],ecx
x = **ppv;
    00B33C63  mov         eax,dword ptr [ppv]  
    00B33C66  mov         ecx,dword ptr [eax]  
    00B33C68  mov         edx,dword ptr [ecx]  
    00B33C6A  mov         dword ptr [x],edx

Вы можете видеть, что там есть дополнительная инструкция mov для разыменования, поэтому я думаю, что двойная разыменование неизбежно медленнее.

Давайте посмотрим на это так:

int var = 5;
int *ptr_to_var = &var;
int **ptr_to_ptr = &ptr;
  • Когда переменная var доступ к вам нужно

    • 1. получить адрес переменной
    • 2. получить его значение с этого адреса.
  • В случае указателя ptr_to_var вам нужно

    • 1. получить адрес переменной указателя
    • 2. получить его значение с этого адреса (то есть, адрес переменной var)
    • 3. выберите значение по указанному адресу.
  • В третьем случае указатель на указатель на int переменная ptr_to_ptr, вам нужно

    • 1. получить адрес указателя на переменную указателя
    • 2. получить его значение с этого адреса (то есть, адрес указателя на переменную ptr_var)
    • 3. Получить выборку из адреса, выбранного на втором шаге (т. Е. Адреса переменной var)
    • 4. выберите значение по указанному адресу.

Таким образом, мы можем сказать, что доступ через указатель на переменную- указатель медленнее, чем доступ к переменной- указателю, который, в свою очередь, медленнее, чем при обычном доступе к переменной.

Другие вопросы по тегам