C ++ загружает и сохраняет оптимизацию и кучу объектов - PullRequest
3 голосов
/ 23 июня 2019

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

Предполагается, что некоторые функции SIMD принимают ссылки на массивы с плавающей точкой. Например,

void do_something(std::array<float, 4>& arr);
void do_something_else(std::array<float, 4>& arr);

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

std::array<float, 4> my_arr{0.f, 0.f, 0.f, 0.f};
do_something(my_arr);
do_something_else(my_arr);
do_something(my_arr);

Оптимизирует ли компилятор c ++ ненужные загрузки и сохраняет между вызовами функций? Имеет ли это значение?

Я видел библиотеки, которые обертывают тип __m128 в структуру и вызывают загрузку в конструкторе. Что происходит, когда вы сохраняете их в куче и пытаетесь вызывать их? Например,

struct vec4 {
    vec4(std::array<float, 4>&) {
        // do load
    }

    __m128 data;
};

std::vector<vec4> my_vecs;
// do SIMD work

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

1 Ответ

2 голосов
/ 24 июня 2019

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

Однако, если компилятор

  1. видит определения функций и их вызовы одновременно (или во время оптимизации времени соединения),

  2. решает встроить вызовы функций и

  3. решает объединить петли,

, тогда он, вероятно, удалит ненужные накопители и нагрузки.

Обратите внимание, что ни одна из трех точек не является тривиальной,Программист контролирует только первую точку, две другие на 100% на усмотрение компилятора.Следовательно, вы, как правило, должны предполагать, что такие оптимизации не происходят.Шансы на встраивание немного возрастают, если ваши функции на самом деле являются шаблонами (что также гарантирует выполнение пункта 1), но не зависит ли компилятор на самом деле от циклов.


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

Однако вы не можете просто псевдоним std::array<float, 4> с __m128, который нарушил бы строгие правила псевдонимов.Реинтерпретация от std::array<float, 4> до __m128 может быть безопасно выполнена только с копией (реинтерпретация до char*, копирование, реинтерпретация до __m128), в противном случае ваш компилятор может смешивать доступ к массиву и типу SIMD.

...