Оптимизация компилятора в c ++ - PullRequest
1 голос
/ 25 февраля 2020

Я пытаюсь реализовать два класса A и B, которые содержат данные, хранящиеся в контейнере std :: unique_ptr, и A может преобразоваться в B с некоторыми вычислениями. Класс A и класс B показаны ниже. Входной параметр конструктора в классе B предназначен для передачи объекта A. Чтобы измерить производительность во время выполнения, я использую библиотеку std :: chrono и добавляю функцию «print_construction_time» в классе B, чтобы показать время построения.

Процессор: Intel® Core ™ i7-6700HQ 2,6 ГГц

ОЗУ: 16 ГБ

ОС: Windows 10 1909

IDE: сообщество Microsoft Visual Studio 2019 Версия 16.4.4

Настройка оптимизации c / c ++: Максимальная оптимизация (скорость фаворита) (/ O2)

class A
{
public:
    A(int input_size, int input_value)              //  constructor
    {
        this->data = std::make_unique<int[]>(input_size);
        this->size = input_size;
        for (int loop_number = 0; loop_number < size; loop_number++) {
            data[loop_number] = input_value;
        }
    }
    std::unique_ptr<int[]> get_data()
    {
        //  deep copy
        auto return_data = std::make_unique<int[]>(size);
        for (int loop_number = 0; loop_number < size; loop_number++) {
            return_data[loop_number] = data[loop_number];
        }
        return return_data;
    }
    int get_size()
    {
        return this->size;
    }
private:
    int size;
    std::unique_ptr<int[]> data;
};
class B
{
public:
    B(A &input_object)                              //  constructor
    {
        this->size = input_object.get_size();
        this->data = std::make_unique<int[]>(this->size);
        this->start = std::chrono::high_resolution_clock::now();
        //  version 1
        for (int loop_number = 0; loop_number < input_object.get_size(); loop_number++) {
            this->data[loop_number] = transform_from_A(input_object.get_data()[loop_number]);
        }
        this->stop = std::chrono::high_resolution_clock::now();  //  for execution time measurement
    }
    std::unique_ptr<int[]> get_data()
    {
        //  deep copy
        auto return_data = std::make_unique<int[]>(size);
        for (int loop_number = 0; loop_number < size; loop_number++) {
            return_data[loop_number] = data[loop_number];
        }
        return return_data;
    }
    void print_construction_time()
    {
        auto duration = std::chrono::duration_cast<std::chrono::microseconds>(stop - start);
        std::cout << "Duration: " << duration.count() << "microseconds" << std::endl;
    }
private:
    int size;
    std::unique_ptr<int[]> data;
    std::chrono::time_point<std::chrono::steady_clock> start, stop;
    int transform_from_A(int input_value)
    {
        return input_value + 1; // For example
    }
};

Основная функция здесь.

int main()
{
    A a_object(10000, 6);
    B b_object(a_object);
    b_object.print_construction_time();

    return 0;
}

Для тестирования я запускаю этот код три раза, и время его выполнения составляет 123407us, 112033us и 107586us. Затем я изменяю блок for l oop в конструкторе класса B на версию 2, разработанную с использованием кэшированных данных.

        //  version 2   <=  tremendously faster than version 1
        auto data_cached = input_object.get_data();
        for (int loop_number = 0; loop_number < input_object.get_size(); loop_number++) {
            this->data[loop_number] = transform_from_A(data_cached[loop_number]);
        }

Результат измерения версии 2 - 27us, 32us и 43us. Мне любопытно, почему компилятор, по-видимому, не выполняет максимальную оптимизацию по скорости автоматически с помощью уловок кэша, основанных на условии того же результата вычисления, и это должно быть сделано человеком. Я думаю, что такого рода оптимизация может быть осуществлена ​​либо компилятором автоматически, либо с помощью предложения изменения в среде редактора.

Примечание. Я также изменяю настройку оптимизации для тестирования. Производительность во время выполнения схожа между настройкой оптимизации в Максимальной оптимизации (скорость фаворита) (/ O2) и Оптимизацией (скорость фаворита) (/Ox).

Фев. 27. Обновление 2020

Я также пытался изменить тип возвращаемого значения A :: get_data () в классе A, добавив const ключевое слово std :: unique_ptr.

    const std::unique_ptr<int[]> get_data()    //    <= Add const keyword of std::unique_ptr
    {
        //  deep copy
        auto return_data = std::make_unique<int[]>(size);
        for (int loop_number = 0; loop_number < size; loop_number++) {
            return_data[loop_number] = data[loop_number];
        }
        return return_data;
    }

Результат измерения времени выполнения такой же, как указано выше. Это версии 101486us, 100538us и 120620us в версии 1 и 55us, 30us и 27us в версии 2. Параметр оптимизации - Оптимизация (Скорость фаворита) (/ Ox), а IDE обновлена ​​до версии 16.4.5.

Более того случай «обоих констант» (не только std :: unique_ptr, но и его содержимого), то есть const std::unique_ptr<const int[]>, также должен быть рассмотрен.

    const std::unique_ptr<const int[]> get_data()
    {
        //  deep copy
        auto return_data = std::make_unique<int[]>(size);
        for (int loop_number = 0; loop_number < size; loop_number++) {
            return_data[loop_number] = data[loop_number];
        }
        return return_data;
    }

Результат измерения времени выполнения также аналогичен приведенному выше , Это 114754us, 127327us и 106122us в версии 1 и 32us, 34us и 44us в версии 2. Параметром оптимизации также является Оптимизация (Скорость фаворита) (/Ox).

...