Подтвердить что ты не робот

Являются ли эти комментарии об оптимизации GCC действительными?

В нижней части Демистификация ограничивающего ключевого слова - это любопытный совет:

Из-за порядка, в котором планирование выполняется в GCC, всегда лучше упрощать выражения. Не смешивать доступ к памяти с расчетами. Код можно переписать следующим образом:

то есть пример, который по существу трансформирует этот

velocity_x[i] += acceleration_x[i] * time_step;

в этот

const float ax  = acceleration_x[i];       // Then the same follows for y, z
const float vx  = velocity_x[i];           // etc for y, z
const float nvx = vx + ( ax * time_step ); // etc
velocity_x[i]   = nvx;                     // ...

Действительно? Я бы подумал, что такое преобразование было тривиальным по сравнению с другими факторами, которые должны были бы оптимизировать компиляторы, такими как аргументы лямбда для std::foreach и т.д.

Это просто устаревший, глупый совет? Или есть веская причина, по которой GCC не может этого или не сделает? (Это заставляет меня беспокоиться о написании выше как velocity += acceleration * time_step с помощью моего класса Vector3f!

4b9b3361

Ответ 1

Изменить: (я удаляю сведения о restrict, потому что он отклоняется от заданного вопроса и вызывает путаницу. OP предполагает, что используется restict.)

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

Вот трансформация, сделанная в документе:

Этот код...

  for (size_t i=0;i<count*stride;i+=stride)
  {
    velocity_x[i] += acceleration_x[i] * time_step;
    velocity_y[i] += acceleration_y[i] * time_step;
    velocity_z[i] += acceleration_z[i] * time_step;
    position_x[i] += velocity_x[i]     * time_step;
    position_y[i] += velocity_y[i]     * time_step;
    position_z[i] += velocity_z[i]     * time_step;
  }

... был преобразован в этот код:

  for (size_t i=0;i<count*stride;i+=stride)
  {
    const float ax  = acceleration_x[i];
    const float ay  = acceleration_y[i];
    const float az  = acceleration_z[i];
    const float vx  = velocity_x[i];
    const float vy  = velocity_y[i];
    const float vz  = velocity_z[i];
    const float px  = position_x[i];
    const float py  = position_y[i];
    const float pz  = position_z[i];

    const float nvx = vx + ( ax * time_step );
    const float nvy = vy + ( ay * time_step );
    const float nvz = vz + ( az * time_step );
    const float npx = px + ( vx * time_step );
    const float npy = py + ( vy * time_step );
    const float npz = pz + ( vz * time_step );

    velocity_x[i]   = nvx;
    velocity_y[i]   = nvy;
    velocity_z[i]   = nvz;
    position_x[i]   = npx;
    position_y[i]   = npy;
    position_z[i]   = npz;
  }

Что такое оптимизация?

Оптимизация не - как было предложено - разделение 1 выражения на 3 выражения.

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

Если вы переходите от данных, перемещающихся с velocity_x[i] до vx в nvx обратно на velocity_x[i], CPU выполняет другую работу между каждым из этих шагов.

Почему это оптимизация?

Современные процессоры обычно имеют конвейерную архитектуру .

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

Почему мой оптимизирующий компилятор не делает это автоматически?

Некоторые делают.

GCC выделяется относительно плохо с этой оптимизацией.

Я разобрал обе петли выше, используя gcc 4.7 (архитектура x86-64, оптимизация при -O3). Аналогичная сборка была произведена, но порядок инструкций был другим, и в первой версии были выпущены значительные киоски, где один поплавок был загружен, изменен и сохранен в пределах нескольких инструкций.

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

Ответ 2

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