Понимание преимуществ семантики перемещения и метапрограммирования шаблонов
Я прочитал несколько описаний о семантике перемещения в С++ 11, и мне интересно, в каком контексте он может быть использован.
В настоящее время многие математические библиотеки С++ используют метапрограммирование шаблонов для задержки оценки.
Если M = A + B + C * D, где M, A, B, C и D являются матричными, шаблонное метапрограммирование позволяет избежать бесполезных копий.
Является ли семантика перемещения более удобной для этого?
Если нет, то в каком контексте используется семантика.
Если да, каковы различия/ограничения по сравнению с метапрограммированием шаблонов для такого использования?
Ответы
Ответ 1
Я не эксперт в отношении этих оптимизаций, но, насколько я понимаю, это отложенные методы оценки, которые вы говорите о работе, определяя арифметические операторы на вашем типе матрицы, такие, что, например, A+B+C*D
не возвращает матрицу, он возвращает прокси-объект, который может преобразовать в матрицу. Это происходит, когда он присваивается M
, а код преобразования будет вычислять каждую ячейку матрицы результатов наиболее эффективными средствами, которые могут возникнуть у дизайнеров библиотеки, избегая временных объектов матрицы.
Итак, предположим, что программа содержит M = A + B + C * D;
Если вы не сделали ничего умного, кроме реализации operator+
обычным способом с помощью operator+=
, вы получите что-то вроде этого, как только нормальный, C + + 03-стиль копия elision вышла в:
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
должен быть оставлен при запуске.
Ответ 2
Я считаю, что более точный термин для того, что вы называете "метапрограммирование шаблонов", это шаблоны выражений.
Если ваши матрицы распределяют свои данные динамически, перемещение семантики может помочь передать эти данные с объекта на объект (в том числе в/из временных), сгенерированных во время выражения, например:
M = A + B + C*D
Шаблоны выражений, с другой стороны, полностью исключат временные файлы.
Если ваши матрицы не распределяют свои данные динамически (например, если они являются фиксированным размером и малыми), перемещение семантики вообще не поможет вашей производительности.
Применение шаблонов выражений к библиотеке матриц приведет к наивысшей производительности. Это также очень сложный метод реализации. Перенос семантики намного проще для реализации и может выполняться в дополнение к шаблонам выражений (если есть такие ресурсы, как память, которые могут быть переданы).
Вкратце:
Перемещение семантики не устраняет временные ограничения, но переносит динамически выделенную память из временных рядов, а не перераспределяет ее.
Шаблоны вытеснения исключают временные файлы.
Ответ 3
Это два разных зверя. Перемещение семантики - это присвоение ресурсов из значения, которое будет уничтожено. При смешивании с шаблонами выражений, например, больших ints (для которых требуется динамическое распределение памяти), можно просто использовать такую память вместо создания копии того, что должно быть уничтожено.
Перемещение семантики также важно для объектов, которые по своей сути не копируются (например, fstream), но имеет смысл сделать перемещаемым.
Ответ 4
Перемещение семантики применимо к ресурсам, управляемым внутри объектов, и используется для того, чтобы избежать ненужного выделения/освобождения ресурсов при создании временных объектов (например, динамически выделенная память является ресурсом).
Метапрограммирование шаблонов работает с структурами, которые выделяются в стеке (так как требуется оценка времени выполнения операндов). Вы можете использовать его, чтобы избежать вычисления времени выполнения для операций, которые можно вычислить во время компиляции.
Ответ 5
Перемещение семантики динамическое, шаблоны выражений - нет. Вы не можете выражать шаблон выражением, которое распространяется по нескольким операторам, и некоторые из них оцениваются только в том случае, если луна синяя, а семантика перемещения может.