Возможны ли эффективные "многократно используемые промежуточные звенья" в программировании шаблонов выражений C ++? - PullRequest
5 голосов
/ 31 августа 2011

Вот одна вещь, которую я не видел явно в программировании шаблонов выражений C ++, чтобы избежать создания ненужных временных объектов (путем создания деревьев «встроенных шаблонных объектов», которые разрушаются только в операторе присваивания).Предположим, что для иллюстрации мы моделируем одномерные последовательности значений с поэлементным применением арифметических операторов, таких как +, * и т. Д. Вызываем базовый класс для полностью созданных последовательностей Seq (который содержит список двойных значений фиксированной длины дляради конкретности) и рассмотрим следующий иллюстративный псевдо-C ++ - код.

void f(Seq &a,Seq &b,Seq &c,Seq &d,Seq &e){
    AType t=(a+2*b)/(a+b+c); // question is about what AType can be
    Seq f=d*t;
    Seq g=e*e*t;
    //do something with f and g
}

, где есть перегрузки с шаблонными выражениями для + и т. д. в других местах.Для строки, определяющей t:

  • , я могу реализовать этот код, если я сделаю AType быть Seq, но затем я создал эту полную промежуточную переменную, когда она мне не нужна (кроме случаев, когдакак это позволяет вычисление f и g).Но, по крайней мере, он рассчитывается только один раз.

  • Я также могу реализовать это, сделав AType подходящим типом шаблонного выражения, чтобы полный Seq не создавался в закомментированной строке, а потреблялсякусок за куском в f и g.Но тогда то же самое вычисление, вовлеченное в создание каждого конкретного чанка, будет повторено и в f и в g.(Я полагаю, что теоретически невероятно умный компилятор мог бы понять, что одни и те же вычисления выполняются дважды, а CSE-it, но я так не думаю, и не хотел бы полагаться на оптимизатора, всегда способного определить возможности.)

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

(я могу смутно представить, что AType мог бы быть каким-то объектом, который содержит как тип шаблона выражения, так и кэшированное значение, которое записывается после его первой оценки, но это некажется, не помогает с необходимостью синхронизировать два неявных цикла в назначениях для f и g.)

В поиске в Google, я натолкнулся на один тезис Мастера по другому предмету, который упоминает прохождение этого руководства "общийисключение подвыражений "следует избегать с помощью шаблонов выражений, но я хотел бы найти болееАвторитетное «это невозможно» или «Вот как это сделать».

Ближайший вопрос stackoverflow - Промежуточные результаты с использованием шаблонов выражений , которые, похоже, касаются проблемы именования типов, а невопрос эффективности в создании полного промежуточного звена.

1 Ответ

2 голосов
/ 31 августа 2011

Поскольку вы, очевидно, не хотите выполнять весь расчет дважды, вам нужно как-то его кешировать.Кажется, для AType проще всего кешировать как Seq.Вы говорите This has the downside of a full intermediate variable,, но это именно то, что вы хотите в этом случае.Это полное промежуточное звено является вашим кешем, и его нельзя избежать тривиально.

Если вы профилируете код, а это критическая точка, то единственный более быстрый способ, который я могу придумать, - написать специальную функцию для вычисления f и g. в параллели , но это было бы очень запутанно и очень не рекомендуется.

void g(Seq &d, Seq &e, Expr &t, Seq &f, Seq &g) 
{
    for(int i=0; i<d.size(); ++i) {
        auto ti = t[i];
        f[i] = d[i]*ti;
        g[i] = e[i]*e[i]*ti;
    }
}
void f(Seq &a,Seq &b,Seq &c,Seq &d,Seq &e) 
{
    Expr t = (a+2*b)/(a+b+c);
    Seq f, g;
    g(d, e, t, f, g);
    //do something with f and g
}
...