C указатели против прямого доступа члена для структур - PullRequest
10 голосов
/ 25 августа 2009

Скажем, у меня есть структура, подобная следующей ...

typedef struct {
  int WheelCount;
  double MaxSpeed;
} Vehicle;

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

double LocalSpeed = MyGlobal.MaxSpeed;

или

double LocalSpeed = pMyGlobal->MaxSpeed;

Одна из моих задач - упростить и исправить недавно унаследованную встроенную систему.

Ответы [ 9 ]

20 голосов
/ 25 августа 2009

В общем, я бы сказал, пойти с первым вариантом:

double LocalSpeed = MyGlobal.MaxSpeed;

Это имеет наименьшее разыменование (вы не находите указатель, затем разыменовываете его, чтобы добраться до его местоположения). Его также проще и легче читать и поддерживать, поскольку вам не нужно создавать переменную-указатель в дополнение к структуре.

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

8 голосов
/ 25 августа 2009

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

на x86 первый перевел бы на что-то вроде этого

mov eax, [address of MyGlobal.MaxSpeed]

а второй будет примерно такой

mov ebx, [address of pMyGlobal] 
mov eax, [ebx+sizeof(int)] 
3 голосов
/ 25 августа 2009
struct dataStruct
{
    double first;
    double second;
} data;

int main()
{
    dataStruct* pData = &data;

    data.first = 9.0;
    pData->second = 10.0;
}

Это вывод сборки с использованием режима выпуска VS2008:

    data.first = 9.0;
008D1000  fld         qword ptr [__real@4022000000000000 (8D20F0h)] 

    pData->second = 10.0;
008D1006  xor         eax,eax 
008D1008  fstp        qword ptr [data (8D3378h)] 
008D100E  fld         qword ptr [__real@4024000000000000 (8D20E8h)] 
008D1014  fstp        qword ptr [data+8 (8D3380h)] 
3 голосов
/ 25 августа 2009

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

Вероятно, существуют гораздо более очевидные области производительности вашей системы.

2 голосов
/ 27 августа 2009

разбирать, разбирать, разбирать ...

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

1) разобрать и осмотреть 2) время исполнения

Как упомянуто выше, хотя суть в том, что это может быть случай двух инструкций вместо одной, которая стоит одного такта, который вы, вероятно, никогда не увидите. Качество вашего выбора компилятора и оптимизатора может привести к гораздо более значительным различиям в производительности, чем попытка изменить одну строку кода в надежде повысить производительность. Переключение компиляторов может дать вам 10-20% в любом направлении, иногда больше. Как и изменение флагов оптимизации, включение всего, что не делает код быстрее, иногда -O1 работает лучше, чем -O3.

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

Используя чужой пример по этому вопросу:

typedef struct
{
    unsigned int first;
    unsigned int second;
} dataStruct;

dataStruct data;

int main()
{
    dataStruct *pData = &data;

    data.first = 9;
    pData->second = 10;

    return(0);
}

С gcc (не таким уж и хорошим компилятором) вы получите:

mov r2, #10
mov r1, #9
stmia   r3, {r1, r2}

Таким образом, обе строки кода C объединены в одном хранилище, проблема здесь в том, что пример используется в качестве теста. Две отдельные функции были бы немного лучше, но для этого нужно гораздо больше кода вокруг, а указатель должен указывать на какую-то другую память, поэтому оптимизатор не понимает, что это статический глобальный адрес, чтобы проверить это, необходимо передать адрес поэтому компилятор (хорошо gcc) не может понять, что это статический адрес.

Или без оптимизации, тот же код, тот же компилятор, без разницы между указателем и прямым.

mov r3, #9
str r3, [r2, #0]

mov r3, #10
str r3, [r2, #4]

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

Так что ответ на ваш вопрос не является абсолютным ... это зависит. разобрать и проверить.

1 голос
/ 25 августа 2009

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

1 голос
/ 25 августа 2009

В C не должно быть различий или незначительного снижения производительности.

C студентов учат:

pMyGlobal->MaxSpeed == (*pMyGlobal).MaxSpeed

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

Если вы ищете оптимизацию производительности, я бы посмотрел в другом месте. С такой микрооптимизацией вы не сможете сэкономить достаточное количество циклов ЦП.

По стилистическим соображениям я предпочитаю нотацию Structure-Dot, особенно при работе с синглтон-глобалами. Я считаю, что читать намного чище.

1 голос
/ 25 августа 2009

Полагаю, что если это вообще что-то изменит, это будет зависеть от архитектуры.

0 голосов
/ 27 августа 2009

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

...