Что означает атрибут [[carry_dependency]]? - PullRequest
67 голосов
/ 20 июня 2011

Может ли кто-нибудь объяснить это на языке, понятном простым смертным?

Ответы [ 2 ]

53 голосов
/ 20 июня 2011

[[carries_dependency]] используется для разрешения переноса зависимостей между вызовами функций.Это потенциально позволяет компилятору генерировать лучший код при использовании с std::memory_order_consume для передачи значений между потоками на платформах со слабо упорядоченными архитектурами, такими как архитектура POWER IBM.

В частности, если значение читается с memory_order_consumeпередается в функцию, затем без [[carries_dependency]], тогда компилятору может потребоваться выполнить команду ограничения памяти, чтобы гарантировать соблюдение соответствующей семантики упорядочения памяти.Если параметр помечен [[carries_dependency]], то компилятор может предположить, что тело функции будет правильно переносить зависимость, и эта граница больше не нужна.

Аналогично, если функция возвращает значение, загруженное с memory_order_consume, или полученное из такого значения, то без [[carries_dependency]] может потребоваться, чтобы компилятор вставил инструкцию ограничения, чтобы гарантировать соблюдение соответствующей семантики упорядочения памяти.С аннотацией [[carries_dependency]] эта граница больше не нужна, так как вызывающая сторона теперь отвечает за поддержание дерева зависимостей.

Например,

void print(int * val)
{
    std::cout<<*p<<std::endl;
}

void print2(int * [[carries_dependency]] val)
{
    std::cout<<*p<<std::endl;
}

std::atomic<int*> p;
int* local=p.load(std::memory_order_consume);
if(local)
    std::cout<<*local<<std::endl; // 1

if(local)
    print(local); // 2

if(local)
    print2(local); // 3

В строке (1)Зависимость является явной, поэтому компилятор знает, что local разыменовывается, и что он должен обеспечить сохранение цепочки зависимостей, чтобы избежать ограничения на POWER.

В строке (2) определениеprint непрозрачен (при условии, что он не встроен), поэтому компилятор должен выставить забор, чтобы гарантировать, что чтение *p в print вернет правильное значение.

On line (3)компилятор может предположить, что, хотя print2 также является непрозрачным, зависимость от параметра до разыменованного значения сохраняется в потоке команд, и для POWER не требуется никаких ограничений.Очевидно, что определение print2 должно фактически сохранять эту зависимость, поэтому атрибут также повлияет на сгенерированный код для print2.

0 голосов
/ 22 января 2014

Короче говоря, я думаю, что если есть атрибут carry_dependency, сгенерированный код для функции должен быть оптимизирован для случая, когда фактический аргумент действительно будет получен из другого потока и будет иметь зависимость. Аналогично для возвращаемого значения. Может быть нехватка производительности, если это предположение неверно (например, в однопоточной программе). Но также отсутствие [[carry_dependency]] может привести к плохой производительности в противоположном случае ... Никаких других эффектов, кроме изменения производительности, быть не должно.

Например, операция разыменования указателя зависит от того, каким образом указатель был получен ранее, и если значение указателя p получено из другого потока (с помощью операции «потребление»), значение, ранее назначенное этим другим потоком для * p, равно: учтено и видно. Может быть другой указатель q, равный p (q == p), но, поскольку его значение не исходит от этого другого потока, значение * q может отличаться от * p. На самом деле * q может спровоцировать своего рода «неопределенное поведение» (потому что доступ к ячейке памяти не согласован с другим потоком, который выполнил назначение).

Действительно, кажется, что в некоторых инженерных задачах есть некоторые большие ошибки в функционировании памяти (и ума) ....>: -)

...