Как избежать сглаживания и повышения производительности?

В этом ответном переполнении стека показано, что сглаживание на С++ может замедлить ваш код. И наложение псевдонимов на С++ относится не только к указателям, но и к ссылкам, а в более общем смысле - к этим типам, указанным в стандарте. В частности, существует

совокупность или тип объединения, который включает один из вышеупомянутых типов среди его членов (включая рекурсивно, член субагрегата или содержащегося объединения)

Итак, согласно моему пониманию, если у меня есть код, как показано ниже,

 class A{
  public:
   int val;
 };

 void foo(vector<A> & array, int & size, A & a0) {
   for(int i=0;i<size;++i) {
    array[i].val = 2*a0.val;
   }
 }

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

  • Тогда мой вопрос - что мне делать с кодом, чтобы избежать наложения псевдонимов и улучшить производительность?
  • Передача const & не поможет, поскольку она не избежит сглаживания, как указано в стандарте. Пропустить a0 по значению? Но это сделает копирование a0, которое мне не нравится, поскольку на практике класс A может быть очень сложным, и копирование является очень дорогостоящим вариантом.
  • Есть ли общее решение, чтобы избежать сглаживания в С++? Если да, то что это такое?

Ответ 1

Вопрос об устранении проблемы производительности псевдонимов на С++, по-видимому, покрывается Проблема рабочей группы Evolution 72: Атрибуты псевдонима N4150: к семантике с ограничениями, подобными псевдониму для С++, N3988 К семантике с ограничениями, подобными слияниям для С++ N3635 К ограничительной семантике для С++ и N4150 был последним вариант предложения. Вопрос EWG еще не разрешен, но, по-видимому, считается готовым к рассмотрению.

В предложении предлагается использовать C как ограничивающие квалификаторы, которые в настоящее время поддерживаются расширениями на С++ многими компиляторами, но имеют нечеткие области, предложение говорит, среди прочего:

Нет никаких сомнений в том, что компилятор преимуществ ограничителя оптимизации во многом, в частности, позволяя улучшить кодовое движение и устранение нагрузок и запасов. С момента введения C99 Ограничение, оно было предоставлено как расширение С++ во многих компиляторах. Но функция хрупкая в С++ без четких правил для синтаксиса С++ и семантика. Теперь с введением С++ 11 функторы заменены лямбдами, и пользователи начали спрашивать, как использовать ограничение в присутствии лямбда. Учитывая существующую поддержку компилятора, мы необходимо предоставить решение с четко определенной семантикой С++, прежде использование некоторого общего подмножества ограничения C99 широко используется в сочетание с конструкциями С++ 11.

предложение также отмечает:

Без стандартизации и улучшения существующего C99 ограничить объект на С++, пользователям, как правило, приходится прыгать через значительные обручи, чтобы получить эффект от переписывание кода через временные или факторинговые и встраивающие тела функций для имитации его эффекта.

Итак, похоже, что в настоящее время нет хорошего решения, хотя в современных компиляторах есть расширения, которые предлагают C ограничить, как семантику, есть много серых областей. 3. Проблемы с ограничением в С++ и C охватывают некоторые из методов, используемых для избежания aliasing, но все они имеют недостатки.

В предложении упоминается N3538, в котором упоминаются некоторые методы и недостатки, связанные с этими методами. Например:

Самый простой способ преодоления псевдонимов - скопировать потенциальный псевдоним.

void rf2(type& output, const type& input) {
    type temp = input;
    output += ra1(temp);
    output += ra2(temp);
}

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

Этот метод применим к вашему делу.

Примечание для интересного взятия на псевдонимы и ограничителя ограничения C99 О избыточности ограничения C99.

Ответ 2

Если цель состоит в том, что вы не ожидаете изменения size или a0.val во время выполнения foo, вы можете сделать это явным, если бы это были локальные значения:

void foo(vector<A> & array, int size, const A & a0) {
    auto new_val = 2*a0.val;
    for(int i=0;i<size;++i) {
        array[i].val = new_val;
    }
}

Теперь ясно, что вы намерены установить все элементы в одно и то же значение. Это понятно читателю, а также компилятору.