2013-04-06 3 views
4

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

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 необходимо.

Заранее благодарю за любую помощь.

EDIT: Результаты экспериментируют решение было предложено Евгений

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

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&& имеет возможность восстановить исходное вычислительное время для случая одной команды.

EDIT: Суммируя соответствующие ссылки, следующие предложения Евгений

Copy Elision

What does auto tell us

Universal References in C++11

C++ Rvalue References Explained

C++ and Beyond 2012: Scott Meyers - Universal References in C++11

+0

Почему арифметическое выражение должно совпадать с циклом 'for'? –

+0

@JoachimPileborg, потому что [шаблоны выражения] (http://eigen.tuxfamily.org/dox-devel/TopicInsideEigenExample.html) будет собирать ** полное ** выражение, оптимизировать его структуру (необязательно) и выполнять его, как это было бы быть ** для ** цикла. http://en.wikipedia.org/wiki/Expression_templates –

ответ

4

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

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

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

A*B+sin(C)+3. 

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


Мой вопрос: есть ли метод (например, с помощью заполнителей?) Признать, что значения D фактически неиспользованными

Такого рода "transfromation":

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

в

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

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

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, но, как я понимаю, автоматически задает компилятор, чтобы определить тип переменной от ее инициализации

Да, точно. Это называется Type Inference/Deduction.

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

Вы знаете, как 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.Не могли бы вы порекомендовать компилятор/Toolset/среду для обеих ОС»для использования 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 .; кажется, что я должен перегрузить оператор = для присвоений между двумя выражениями, правильно?

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

В принципе, он просто называет временное значение и продлевает срок его службы до конца области. Check this SO.

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

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

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

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

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

+0

Большое спасибо за ваш ответ. У меня уже есть библиотека матриц, использующая шаблоны выражений, написанные на C++ и CUDA для GPGPU. Я не знаком с C++ 11, но, как я понимаю, 'auto' просит компилятор определить тип переменной из ее инициализации: очень полезно с шаблонами выражений. Вы знаете, как CUDA и C++ 11 взаимодействуют друг с другом? Кроме того, вы могли бы лучше расширить свой последний пункт (истечение срока действия «rvalue')? Означает ли это, что я не мог «опубликовать» -оценку «D», если понадобится впоследствии? Наконец, есть ли возможность только с C++ (избегая C++ 11)? Очень хороший момент! Спасибо! – JackOLantern

+0

Я обновил ответ. Кстати, какой компилятор/набор инструментов/среда вы используете? –

+0

Еще раз спасибо. +1 и принятый ответ. Раньше я использовал 'nvcc/gcc' под' Linux', но по некоторым причинам я теперь использую 'CUDA/Visual Studio 2010' под' Windows'. Не могли бы вы рекомендовать компилятор/набор инструментов/среду для обеих ОС использовать 'C++ 11' в рамках моего интереса (GPGPU и CUDA, вы знаете какие-либо). Кроме того, трюк 'auto && intermediate = get_large_rvalue();' похоже, не является «прозрачным» для стороннего пользователя (который не должен знать такую ​​проблему). Я прав? Любая альтернатива? – JackOLantern

1

В 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 (на самом деле п раз, где п размер матриц). Если D - простой матричный тип, это не проблема. Если D - это выражение, вы оцениваете его много, много раз.

На contray, делая

F = D + E 

прекрасно.

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

+1

Библиотеки шаблонов выражений могут автоматически распознавать, когда лучше оценивать часть результата во временное, а когда нет. Например, [Eigen делает это] (http://eigen.tuxfamily.org/dox/TopicLazyEvaluation.html): ** «Eigen автоматически определяет для каждого подвыражения, следует ли его оценивать во временную переменную. Действительно, в некоторых случаях лучше сразу оценить подвыражение во временную переменную, в то время как в других случаях это лучше избегать этого ». ** –

+0

@sbabbi Большое спасибо за ваш ответ. Пожалуйста, см. Также мой комментарий к Евгению Панасюку. На самом деле, я не понимаю, почему 'F = D * E' должно отличаться от' F = D + E'. Оба метода связаны с элементами. Не могли бы вы это объяснить? Благодарю. – JackOLantern