Понимание преимуществ семантики перемещения по сравнению с метапрограммированием шаблонов

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

Если M = A + B + C*D, где M, A, B, C и D — матрицы, шаблонное метапрограммирование позволяет избежать бесполезных копий. Является ли семантика перемещения более удобным способом делать подобные вещи?

Если нет, то в каком контексте используется семантика перемещения. Если да, то в чем разница/ограничения по сравнению с метапрограммированием шаблонов для такого использования?


person Vincent    schedule 23.05.2012    source источник
comment
метапрограммирование — это полная противоположность задержке оценки. Интересный вопрос, но образец ваших мыслей был бы полезен.   -  person Captain Giraffe    schedule 23.05.2012
comment
‹предположение› Я думаю, что разница между этими двумя подходами не будет большой, но я думаю, что ссылки на rvalue сделают библиотеку немного проще и чище. ‹/предположение›   -  person Mooing Duck    schedule 23.05.2012
comment
@MooingDuck: шаблоны выражений иногда позволяют больше, чем просто удалять копии. Например, в выражении A*B+C шаблон выражения может оптимизировать вычисления, вставляя инструкции FMA и т. д.   -  person André Caron    schedule 23.05.2012
comment
@AndréCaron: Я знаю об этом, я предполагал, что программирование такого зверя может быть проще закодировать с помощью ссылок rvalue, чем TMP. Говард пояснил, что rvalue в этом случае не помогает.   -  person Mooing Duck    schedule 23.05.2012


Ответы (5)


Я не эксперт по этим оптимизациям, но, насколько я понимаю, методы отложенной оценки, о которых вы говорите, работают, определяя арифметические операторы для вашего типа матрицы, так что, например, A+B+C*D не возвращает матрицу, он возвращает прокси-объект который может преобразовываться в матрицу. Это происходит, когда ему присвоено значение M, и код преобразования будет вычислять каждую ячейку результирующей матрицы наиболее эффективными способами, которые могут придумать разработчики библиотеки, избегая временных объектов матрицы.

Итак, предположим, что программа содержит M = A + B + C * D;

Если бы вы не сделали ничего умного, кроме как реализовали operator+ обычным способом, используя operator+=, вы бы получили что-то вроде этого, как только сработает нормальное копирование в стиле C++03:

Matrix tmp1 = C;
tmp1 *= D;
Matrix tmp2 = A;
tmp2 += B;
tmp2 += tmp1;
M = tmp2;

С отложенной оценкой вы можете получить что-то вроде:

for (int i = 0; i < M.rows; ++i) {
    for (int j = 0; j < M.cols; ++j) {
        /* not necessarily the best matrix multiplication, but serves to illustrate */
        c_times_d = 0;
        for (int k = 0; k < C.cols; ++k) {
            c_times_d += C[i][k] * D[k][j];
        }
        M[i][j] = A[i][j] + B[i][j] + c_times_d;
    }
}

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

Насколько мне известно, семантика перемещения в этом случае мало помогает. Ничто из того, что вы написали, не позволяет нам перейти от A, B, C или D, поэтому мы получим эквивалент:

Matrix tmp1 = C;
tmp1 *= D;
Matrix tmp2 = A;
tmp2 += B;
tmp2 += std::move(tmp1);
M = std::move(tmp2);

Таким образом, семантика перемещения не помогла ни с чем, кроме последнего бита, где, возможно, версии операторов rvalue лучше, чем обычные. Доступно больше, если вы напишете std::move(A) + std::move(B) + std::move(C) * std::move(D), потому что нам не пришлось бы копировать из C или A, но я все равно не думаю, что результат будет таким же хорошим, как код с отложенной оценкой.

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

1) при отложенной оценке промежуточные результаты никогда не должны существовать в виде полных матриц. Семантика перемещения не спасает компилятор от создания полной матрицы A+B в памяти в какой-то момент.

2) с отложенным вычислением мы можем начать изменять M до того, как закончим вычисление всего выражения. Семантика перемещения не помогает компилятору переупорядочивать модификации: даже если компилятор достаточно умен, чтобы определить потенциальную возможность, модификации невременных объектов должны храниться в правильном порядке, если существует опасность выбрасывается исключение, потому что если какая-либо часть A + B + C * D выбрасывается, то M нужно оставить в том виде, в котором она началась.

person Steve Jessop    schedule 23.05.2012

Я считаю, что более точным термином для того, что вы называете "метапрограммированием шаблонов", является шаблоны выражений.

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

M = A + B + C*D

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

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

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

В итоге:

Семантика перемещения не устраняет временные объекты, но будет передавать динамически выделенную память между временными объектами вместо ее перераспределения.

Шаблоны выражений исключают временные элементы.

person Howard Hinnant    schedule 23.05.2012

Это два разных зверя. Семантика перемещения связана с присвоением ресурсов из значения, которое будет уничтожено. При смешивании с шаблонами выражений, скажем, больших целых чисел (которые требуют динамического выделения памяти), можно было бы просто присвоить такую ​​память вместо того, чтобы делать копию чего-то, что вот-вот будет уничтожено.

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

person K-ballo    schedule 23.05.2012

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

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

person Attila    schedule 23.05.2012

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

person Puppy    schedule 23.05.2012