Шаблоны выражений: повышение производительности при оценке выражений?

По методике шаблонов выражений матричное выражение типа

D = A*B+sin(C)+3.;

с точки зрения производительности вычислений в значительной степени эквивалентен рукописному for петля.

Теперь предположим, что у меня есть следующие два выражения

D = A*B+sin(C)+3.;
F = D*E;
cout << F << "\n";

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

Мой вопрос: есть ли метод (например, с использованием заполнителей?), Чтобы признать, что значения D на самом деле не используются, и что представляющие интерес ценности являются единственными элементами F, так что только выражение

F = E*(A*B+sin(C)+3.);

оценивается, и вся производительность эквивалентна производительности одного for цикл?

Конечно, такая гипотетическая техника также должна быть в состоянии вернуться обратно, чтобы оценить выражение

D = A*B+sin(C)+3.;

если позже в коде значения D необходимы.

Спасибо заранее за любую помощь.

РЕДАКТИРОВАТЬ: Результаты экспериментов решение, предложенное Евгением

Оригинальная инструкция:

Result D=A*B-sin(C)+3.;

Время вычислений: 32 мс

Инструкция в два этапа:

Result Intermediate=A*B;
Result D=Intermediate-sin(C)+3.;

Время вычислений: 43мс

Решение с auto:

auto&& Intermediate=A*B;
Result D=Intermediate-sin(C)+3.;

Время вычислений: 32мс.

В заключение, auto&& Позволяет восстановить исходное время вычислений в случае одной команды.

РЕДАКТИРОВАТЬ: Обобщение соответствующих ссылок, следуя предложениям Евгения

Копировать Elision

Что говорит нам авто

Универсальные ссылки на C ++ 11

C ++ Rvalue Ссылки объяснил

C ++ и последующий 2012 год: Скотт Мейерс — Универсальные ссылки на C ++ 11

4

Решение

Оценка шаблон выражения обычно происходит, когда вы сохраняете результат в некоторых специальный типа как:

Result D = A*B+sin(C)+3.;

Тип результата выражения:

A*B+sin(C)+3.

не является Результат, но это то, что можно конвертировать в Результат. И оценка происходит во время такой конверсии.


Мой вопрос: есть ли метод (например, использование заполнителей?), Чтобы признать, что значения D на самом деле не используются

Такой вид «трансформации»:

Result D = A*B+sin(C)+3.;
Result F = D*E;

в

Result F = (A*B+sin(C)+3.)*E;

Возможно, когда вы не оцениваете D. Чтобы сделать это, как правило, вы должны захватить D, как это реально, выражение тип. Например, с помощью авто:

auto &&D = A*B+sin(C)+3.;
Result F = D*E;

Тем не менее, вы должны быть осторожны — иногда шаблон выражения захватывает ссылки на его операнды, и если у вас есть некоторые Rvalue который истекает после его выражения:

auto &&D = A*get_large_rvalue();
// At this point, result of **get_large_rvalue** is destructed
// And D has expiried reference
Result F = D*E;

куда get_large_rvalue является:

LargeMatrix get_large_rvalue();

Это результат Rvalue, истекает в конце полного выражения, когда get_large_rvalue назывался. Если что-то в выражении будет хранить указатель / ссылку на него (для последующей оценки), и вы будете «откладывать» оценку — указатель / ссылка переживут указанный / ссылочный объект.

Чтобы предотвратить это, вы должны сделать:

auto &&intermediate = get_large_rvalue(); // it would live till the end of scope
auto &&D = A*intermediate ;
Result F = D*E;

Я не знаком с C ++ 11, но, как я понимаю, auto просит компилятор определить тип переменной по ее инициализации

Да, точно. Это называется Тип Вывод / Дедукция.

В C ++ 98/03 было приведение типа только для шаблонных функций, в C ++ 11 есть авто.

Знаете ли вы, как CUDA и C ++ 11 взаимодействуют друг с другом?

Я не использовал CUDA (хотя я использовал OpenCL), но я думаю, что не будет никаких проблем в хозяин код с C ++ 11. Возможно, некоторые функции C ++ 11 не поддерживаются устройство код, но для вашей цели — вам нужно авто только в хозяин код

Наконец, есть ли возможность только с C ++?

Вы имеете в виду pre-C ++ 11? То есть C ++ 98 / C ++ 03?
Да, это возможно, но у него больше синтаксического шума, возможно, это будет причиной для его отклонения:

// somehwhere
{
use_D(A*B+sin(C)+3.);
}
// ...
template<typename Expression>
void use_D(Expression D) // depending on your expression template library
//   it may be better to use (const Expression &e)
{
Result F = D*E;
}

Я сейчас использую CUDA / Visual Studio 2010 под Windows. Не могли бы вы порекомендовать компилятору / набору инструментов / среде для обеих ОС использовать C ++ 11 в рамках моих интересов (GPGPU и CUDA, вы знаете, что-нибудь)

MSVC 2010 поддерживает некоторые части C ++ 11. В частности это поддерживает авто. Так что, если вам нужно только авто из C ++ 11 — MSVC2010 в порядке.

Но если вы можете использовать MSVC2012 — я бы порекомендовал придерживаться его — он имеет гораздо лучшую поддержку C ++ 11.

Кроме того, трюк авто &&промежуточный = get_large_rvalue (); кажется не «прозрачным» для стороннего пользователя (который не должен знать о такой проблеме). Я прав? Любая альтернатива?

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

LargeMatrix temp = get_large_rvalue();

Или, может быть, даже глобальная / статическая переменная (менее предпочтительный метод).

Последний комментарий / вопрос: использовать авто &&D = A * B + sin (C) +3 .; кажется, что я должен перегрузить оператор = для присваиваний между двумя выражениями, верно?

Нет, такая форма не требует ни оператора копирования / перемещения, ни конструктора копирования / перемещения.

По сути, он просто называет временное значение и продлевает срок его службы до конца области действия. Проверьте это ТАК.

Но, если бы вы использовали другую форму:

auto D = A*B+sin(C)+3.;

В таком случае для компиляции может потребоваться конструктор копирования / перемещения / преобразования (хотя фактическая копия может быть оптимизирована компилятором с помощью Копировать Ellision)

Кроме того, переключение между использованием auto (для промежуточных выражений) и Result to force вычисления кажется непрозрачным для стороннего пользователя. Любая альтернатива?

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

4

Другие решения

В C ++ 11 вы можете использовать auto

auto D = A*B+sin(C)+3.;

при условии, что вы используете шаблоны выражений, тип D будет <some template type which represents an expression>,
Теперь вы должны использовать это осторожно, потому что вы экономите часть памяти (нет необходимости выделять место для матрицы), но в зависимости от того, как вы ее используете, это может быть не лучшим.

Подумать о

F = D * E

Элемент D [i] [j] необходимо многократно «посещать» при вычислении D * E (фактически n раз, где n — размер матриц). Если D — тип с простой матрицей, это не проблема. Если D является выражением, вы оцениваете его много, много раз.

На контрай, занимаюсь

F = D + E

Это хорошо.

Подумайте об этом: вы не можете написать F = E*(A*B+sin(C)+3.); используя только два вложенных цикла.

2

По вопросам рекламы [email protected]