Название: Шаблоны выражений Отправлено: m_ax от Апрель 06, 2012, 11:34 Введение
В данной заметке речь пойдёт об одном из важных применений шаблонного метапрограммирования, а именно о т.н. шаблонах выражений. Шаблоны выражений позволяют кардинально оптимизировать некоторые виды вычислений и при этом сохранить естественную запись математических выражений. Впервые такое применение шаблонам было найдено в 1994 г. Тоддом Вельдхузеном и Дэвидом Вандерворде. В последствии этот приём был положен в основу таких мат. библиотек как: Blitz Matrix Template Library POOMA boost ublas и др. Для большей ясности, зачем всё это нужно, рассмотрим типичные операции линейной алгебры, такие как сложение, вычитание векторов: V = c1*V1 + c2*V2 + c3*V3 + c4*V4, где V1, V2, V3, V4 - вектора, а c1, c2, c3 и c4 - числа. В примитивной реализации этого выражения создаются временные объекты: вначале для c1*V1, c2*V2, c3*V4 и c4*V4. Затем будут созданы временные объекты (c1*V1 + c2*V2) и (c3*V3 + c4*V4) и в конце будет создан ещё один временный объект для последней суммы. Если вектора имеют большой размер то затраты на создание и уничтожение временных объектов могут оказаться неприемлемыми. Но с использованием шаблонов выражений можно писать подобные мат. конструкции без создания временных объектов. Я покажу на примере класса vector, как это можно реализовать. Реализация Шаблонов выражений на примере класса vector И так, вначале приведу реализацию класса vector. Код
Здесь следует обратить внимание на пару тонких моментов. Во-первых, вначале идёт предварительное объявление шаблонного класса expr, который фактически и будет являться шаблоном выражения. Его реализация будет описана ниже. Во-вторых, появились дополнительные операторы: Код
которые принимают в качестве аргумента наше шаблонное выражение. Теперь посмотрим на реализацию класса expr: Код Первые два параметра шаблона T и N - определяют тип элементов вектора и его длину. Далее идут параметры Left и Right - это левое и правое выражения, которые нужно объединить в одно выражение. Последний параметр F - задаёт бинарную функцию(std::plus<T>, std::minus<T>, std::multiplies<T> и т.д.) в зависимости от того, какая операция должна быть произведена над выражениями Left и Right. Конструктор класса expr просто копирует ссылки на объекты Left и Right. Далее объявляется оператор[] в котором с помощью объекта бинарной функции конструируется итоговое выражение. Чтоб было понятно, как это всё работает рассмотрим операцию сложения двух векторов. Для этого определим оператор+ Код
Возвращаемое значение в данном случае представляет из себя не вектор, а шаблонное выражение. при этом временных объектов не создаётся. при создании expr в его конструктор передаются ссылки, которые он просто копирует. Однако это ещё не всё.. Сейчас мы уже можем писать выражения типа: V = V1+V2 и это будет работать. Но мы пока не можем написать выражения типа: V = V1+V2+V3+... Для этого нужно определить ещё такие операторы+, а именно: 1) вектор + выражение 2) выражение + вектор 3) выражение + выражение Приведу лишь первый вариант (остальные пишутся аналогично) Код Теперь, после того, как мы реализовали все 4 оператора+ мы спокойно можем писать любые комбинации, наподобии следующих: v = v1+(V2+V3)+V4+(V5+V6+V7)+V8, и т.д. Теперь, если аналогично определить оператор- (минус) то мы почти будем у цели) Для этого оператора всё в точности аналогично, нужно лишь везде поменять std::plus<T> на std::minus<T> Код
Теперь осталось реализовать лишь оператор умножения вектора на число и деление на число. Предлагаю это проделать в качестве самостоятельного задания) После полной реализации всех операторов мы получаем возможность писать абсолютно любые выражения с нашим вектором. И самое главное, что ещё на этапе компиляции выражение, например вида: Код
будет развёрнуто в конструкцию вида: Код причём без создания и уничтожения дополнительных временных объектов. Надеюсь, это было кому то полезным) Исходники с примером приатачены. Название: Re: Шаблоны выражений Отправлено: Igors от Апрель 06, 2012, 17:10 После полной реализации всех операторов мы получаем возможность писать абсолютно любые выражения с нашим вектором. И самое главное, что ещё на этапе компиляции выражение, например вида: Ну Вы знаете, достигнутый эффект выглядит слишком мал по сравнению с затраченными усилиями :) Но в конце-концов почему бы мне не подучить template (которые я знаю плохо) если есть человек который мне охотно объясняет?Код C++ (Qt) v = a*v1 + a2*(v2+a3*v3)-(v4+v5) + v6*a4 будет развёрнуто в конструкцию вида: Код C++ (Qt) for (size_t i = 0; i < v.size(); ++i) v = a*v1 + a2*(v2+a3*v3)-(v4+v5) + v6*a4 причём без создания и уничтожения дополнительных временных объектов. Правда пока получается не очень. Пытаясь осмыслить напр такую соплю Код я сразу же смертельно устаю :) Ну не все сразу, по крайней мере я уже знаю: в теории избавиться от промежуточных значений можно. Уже хорошо! Два момента 1) Ну что с именами у Вас никак не ладится :'( Ну как можно было запрягать имя vector? Назвали бы хоть array, что ли.. 2) Есть "векторный процессор" (наск я помню называется SSL). это выглядит это так Код Мне приходилось писать такой спец код лет 10 назад, так что могу и наврать. Но прирост в производительности там хороший (в разы). А как с этим в Вашей реализации? А в целом - хороший, конструктивный пост (побольше бы таких). Спасибо Название: Re: Шаблоны выражений Отправлено: m_ax от Апрель 06, 2012, 17:48 Цитировать Ну Вы знаете, достигнутый эффект выглядит слишком мал по сравнению с затраченными усилиями Ну я бы не сказал.. Вообще спорно сравнивать усилия от написания нескольких дополнительных строчек кода (один раз) с усилиями приложенными в последующем, городить циклы. В последнем случае мы проигрываем в читаемости кода, повышаем риск ошибок, да и время это занимает больше (циклы писать). Код
Здесь всё не так сложно.. Дело привычки) Этот оператор возвращает объект expr<T, N, Left, Right, F> где: T - тип элементов в векторе N - размер вектора Left = vector<T, N> Right = vector<T, N> F = std::minus<T> что означает: нужно взять Left и Right и применить к ним операцию std::minus<T> Цитировать Мне приходилось писать такой спец код лет 10 назад, так что могу и наврать. Но прирост в производительности там хороший (в разы). А как с этим в Вашей реализации? Про SSL ничего сказать не могу, поскольку не сталкивался.В данном случае, на этапе компиляции происходит разворачивание изначального выражения в цикл. Поэтому разницы по скорости выполнения кода, для вручную написанного цикла и тем, что делает этот механизм, очевидно нет. А вот разница между примитивным вариантом с созданием временных объектов - колосальна и пропорциональна сложности изначального выражения и размера векторов, участвующих в этом выражении. |