Допустим, у меня есть этот класс

class Point 
{
  inline float x() const { return v[0]; }
  inline float y() const { return v[1]; }
  inline float z() const { return v[2]; }

  float v[3];
};

И я делаю:

Point myPoint;
myPoint[0] = 5;

// unrelated code goes here

float myVal = myPoint.x() + 5;

Будет ли GCC -O2 или -O3 оптимизировать любые вызовы x(), просто получив v[0]? IE :

float myVal = myPoint.v[0] + 5;

Или есть причина, почему это невозможно?

Обновление: Стоит упомянуть, что я понимаю, что inline - это скорее предложение для компилятора, чем что-либо еще, но все равно интересно.

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

3
Tyler Shellberg 5 Фев 2020 в 00:48

3 ответа

Лучший ответ

Это может или не может быть встроено. Никаких гарантий там нет. Но если вы хотите, чтобы он всегда был встроенным, используйте атрибут [[gnu::always_inline]]. См. здесь. Используйте этот атрибут, только если вы знаете, что делаете. В большинстве случаев лучше позволить компилятору решить, какие оптимизации подходят.

2
Ayxan 4 Фев 2020 в 22:14

Будет ли GCC оптимизировать встроенный метод доступа?

Все оптимизирующие компиляторы так и сделают. Это тривиальная оптимизация по сравнению с другими.

Или есть причина, почему это невозможно?

Нет причин, которые делают это невозможным, но нет и гарантии.

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

Нет. Но, конечно, компилятор может иметь разные пороговые значения для шаблонов.

6
Acorn 4 Фев 2020 в 22:11

Вы можете увидеть разницу здесь: https://godbolt.org/

Допустим, у вас есть этот код (ваш код не компилируется: отсутствует ;, а Point нет []):

struct Point 
{
  inline float x() const { return v[0]; }
  inline float y() const { return v[1]; }
  inline float z() const { return v[2]; }

  float v[3];
};

int main() {
    Point myPoint;
    myPoint.v[0] = 5;

    float myVal = myPoint.x() + 5;
    return myVal;
}

Затем gcc 9.2 испускает:

Point::x() const:
        push    rbp
        mov     rbp, rsp
        mov     QWORD PTR [rbp-8], rdi
        mov     rax, QWORD PTR [rbp-8]
        movss   xmm0, DWORD PTR [rax]
        pop     rbp
        ret
main:
        push    rbp
        mov     rbp, rsp
        sub     rsp, 16
        movss   xmm0, DWORD PTR .LC0[rip]
        movss   DWORD PTR [rbp-16], xmm0
        lea     rax, [rbp-16]
        mov     rdi, rax
        call    Point::x() const
        movss   xmm1, DWORD PTR .LC0[rip]
        addss   xmm0, xmm1
        movss   DWORD PTR [rbp-4], xmm0
        movss   xmm0, DWORD PTR [rbp-4]
        cvttss2si       eax, xmm0
        leave
        ret
.LC0:
        .long   1084227584

Я не настолько опытен в чтении ассемблера, но я сравниваю вышесказанное с выводом с -O3

main:
        mov     eax, 10
        ret

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

Приведенный выше пример уже опровергает «никогда». «Всегда», однако получить трудно. Гарантия, которую вы получаете, заключается в том, что результирующий код ведет себя так, как если бы компилятор транслировал ваш код без применения оптимизаций. Оптимизация обычно не гарантируется, за некоторыми исключениями. Чтобы быть действительно уверенным, я бы полагался только на то, чтобы смотреть на вывод компилятора в реалистичном сценарии.

2
idclev 463035818 4 Фев 2020 в 22:13