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

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

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

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

5 ответов

Решение

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

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

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

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 должен быть оставлен, как это началось.

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

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

M = A + B + C*D

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

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

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

В итоге:

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

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

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

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

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

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

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

Другие вопросы по тегам