Подтвердить что ты не робот

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

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

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

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

4b9b3361

Ответ 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

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