Как я могу развернуть цикл, используя метапрограммирование C ++? - PullRequest
2 голосов
/ 09 декабря 2011

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

template <int N, typename ValueType>
struct ScalarProduct {
    static ValueType product (ValueType* first, ValueType* second) {
        return ScalarProduct<N-1, ValueType>::product(first + 1, second + 1) 
            + *first * *second;
    }
};

template <typename ValueType>
struct ScalarProduct<0, ValueType> {
    static ValueType product (ValueType* first, ValueType* second) {
        return 0;
    }

тогда время вычислений во время выполнения меньше, чем во время компиляции

Ответы [ 3 ]

3 голосов
/ 09 декабря 2011

Во-первых, вы пишете функции.Таким образом, метапрограммирование или нет, компилятор будет генерировать функции.И поскольку функции не будут оцениваться до времени выполнения, ваш подход не приведет к сокращению времени выполнения.На самом деле, это может добавить немного накладных расходов, когда вы развертываете цикл for в рекурсивном вызове функции.

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

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

Вы можете работать с произвольными массивами, только если они уже определены в вашем коде.Например,

int a1[] = {1,2,3};
int a2[] = {2,4,5};

template <int N,typename T>
struct foo {
  int product;
  foo<N-1,T> rest;
  foo(const T* array1,const T* array2) : rest(array1+1,array2+1) { product = array1[0] * array2[0] + rest.product; }
};

template <0,typename T>
struct foo {
  int product;
  // These addresses are stale, so don't use them
  foo(cons T* array1, const T* array2) : product(0) {}
};

foo<3,int> myfoo(a1,a2);

И вы можете использовать myfoo.product для получения значения перекрестного произведения a1 и a2, вычисленного во время компиляции.

0 голосов
/ 09 декабря 2011

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

Если бы мне пришлось угадывать, я бы сказал, что ваше умное рекурсивное расширение шаблона создает слишком сложный коддля вашего компилятора для эффективной оптимизации.Циклы над массивами с плавающей точкой, возможно, являются наиболее агрессивно оптимизированной конструкцией в C ++;Ваш компилятор может даже иметь специальные случаи для скалярных продуктов.Конечно, он может развернуть свои собственные циклы, если захочет.

В простых вещах, подобных этим, лучше не пытаться обмануть компилятор, выполняя необходимые оптимизации.Он понимает скалярные продукты лучше, чем вы, и лучше оснащен, чтобы эффективно решать вашу проблему.На самом деле!

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

Удачи.

0 голосов
/ 09 декабря 2011

Компилятор может сделать это за вас, но не может. Вы должны посмотреть на код, сгенерированный компилятором, чтобы выяснить, делает ли он это или нет. Если по какой-то причине это не так, ваш единственный вариант - не использовать рекурсивный шаблон и придумать альтернативное решение, то есть таблицу поиска, цикл выполнения и т. Д.

...