Могут ли арифметические операторы C ++ c агрессивно оптимизироваться в пользовательских классах? - PullRequest
0 голосов
/ 12 апреля 2020

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

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

Предоставляет ли спецификация C ++ некоторый механизм для достижения в операторах ваших собственных классов те же шансы оптимизации, что и для встроенных целочисленных типов и типов с плавающей запятой?

Представьте, например, что у вас есть следующее:

#include <cstdint>

class MyFP16
{
private:
    std::uint16_t m_val;

public:

    MyFP16();
    [...other constructors here...]
    ~MyFP16();

    // Arithmetic operators
    friend MyFP16 operator+(const MyFP16 &c1, const MyFP16 &c2);
    friend MyFP16 operator-(const MyFP16 &c1, const MyFP16 &c2);
    friend MyFP16 operator*(const MyFP16 &c1, const MyFP16 &c2);
    friend MyFP16 operator/(const MyFP16 &c1, const MyFP16 &c2);

    [...rest of arithmetic operators...]

    // Other logic needed
    [...]
};

Можно ли определить этот класс таким образом, чтобы все операторы имели точно такую ​​же семантику, что и во встроенном типе float, чтобы все оптимизации арифметических c выражений, которые можно использовать для float, можно использовать и для моего класса, используя преимущества переупорядочения операций, коммутативности / ассоциативности, преобразования одних операций в другие, результатов констант предварительного вычисления и т. д. c .. .? Как?

Большое спасибо!

1 Ответ

1 голос
/ 12 апреля 2020

Ничего особенного в перегрузках операторов или пользовательских литералах нет.

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

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

Если встроена небольшая функция, вы вернетесь к выражениям, которые работают с примитивными типами, и компилятор может использовать те же методы оптимизации, которые вы уже упоминали.

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

Также убедитесь, что вы пометили каждую функцию, которой разрешено пометить constexpr как таковую, чтобы было легче выполнять оценку во время компиляции и писать код в constexpr -дружественный способ (т. Е. Используйте только литеральные типы).

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...