Насколько интеллектуален шаблонизатор шаблонов g ++ (избегая раздувания шаблонов) - PullRequest
2 голосов
/ 19 февраля 2012

Если у меня есть шаблон с кучей другого кода в нем.Будет ли g ++ повторно генерировать весь этот код, который одинаков для каждой версии шаблона?

Например:

template <typename> T
T parseSomething(const std::string& data) {
    // Some state variables go here
    enum State {state1,state2,state3} state;
    for(std::string::const_iterator i=data.begin();i!=data.end();++i) {
        // Some big testy stuff to see if we got in the right place
        switch (state) {
            case state1: {
                switch (*i) {
                    case f:  // ...
        // ... lots of switchy stuff here ..
        return T(*i);
    }
}

Так что в этой функции ... единственное, что действительно нуждается в шаблонах, этострока возврата T (* i).

Предположим, что я создал его с 4 различными Ts, например.

parseSomething<float>(data);
parseSomething<int>(data);

и т. д.

Будет ли g ++ генерировать весь этот другой код (цикл и части переключателя) отдельное время для каждого T?

Или это было бы достаточно разумно, чтобы генерировать переключатели и циклы только один раз ... затем для каждого T .. генерировать возврат T (* i);line?

Я пробовал тестировать, и с -O0 он определенно дублирует переключатели везде, но с -O2 и выше было трудно сказать;это оказалось умнее ... но это было так умно, я не мог расшифровать ASM:)


Вот мой пример программы, которую я пытаюсь использовать для тестирования.

Для компиляции:

g++ -std=c++0x -fverbose-asm -ggdb3 -fvar-tracking-assignments  -O6 -march=native  codegen.cpp

для запуска:

gdb --args ./a.out asdf1111

версия параноидального кода:

#include <iostream>
#include <string>

using namespace std;

char getSomething(const string& myString) {
    for(auto myPlase=myString.begin();myPlase!=myString.end();++myPlase) {
        if (*myPlase == 'f') {
            return *(myPlase+1);
        }
    }
}

template <typename T>
T getSomething(const string& myString) {
    return T(getSomething(myString));
}

int main(int argc, char** argv) {
    string base = argv[1];
    float myFloat = getSomething<float>(base);
    int myInt = getSomething<int>(base);
    char myChar = getSomething<char>(base);
    //string newString = getSomething<string>(base);
    cout << myFloat << " " << myInt << " " << myChar << endl;
}

версия кода, которую я хотел бы использовать:

#include <iostream>
#include <string>

using namespace std;

template <typename T>
T getSomething(const string& myString) {
    for(auto myPlace=myString.begin();myPlace!=myString.end();++myPlace) {
        if (*myPlace == 'f') {
            return T(*(myPlace+1));
        }
    }
}

int main(int argc, char** argv) {
    string base = argv[1];
    float myFloat = getSomething<float>(base);
    int myInt = getSomething<int>(base);
    char myChar = getSomething<char>(base);
    //string newString = getSomething<string>(base);
    cout << myFloat << " " << myInt << " " << myChar << endl;
}

Ответы [ 2 ]

4 голосов
/ 19 февраля 2012

Я не думаю, что компиляторы достаточно умны, чтобы объединять код, который не зависит от параметра шаблона. Другими словами, функция будет реализована 4 раза, один раз для каждого T.

В linux вы можете использовать readelf -s в результирующем объектном файле для вывода открытых символов и readelf -S для вывода разделов; каждая не встроенная нестатическая функция будет иметь (искаженную) запись в таблице символов. AFAIK, каждый шаблон шаблона находится в отдельном разделе, так что их можно объединить во время соединения.

1 голос
/ 20 февраля 2012

Довольно редко иметь большой блок непараметрического кода внутри шаблона, будь то класс или функция.

Нетрудно обнаружить большой кусок кода, который не зависит от параметров. Движок должен был бы просто записать некоторую метрику размера поддерева в соответствующих узлах AST и определить, является ли какой-либо дочерний узел параметром шаблона.

Но оптимизация, которую вы предлагаете, по существу требует отделения внутренних областей от внешних областей, что означает рефакторинг их в новую функцию. Что если вместо временной у вас была именованная переменная, время жизни которой включало внутреннюю switch? Стек будет переупорядочен, внутренние области будут зависеть от переменной, несмотря на то, что, возможно, не ссылаются на нее, и локальные переменные должны быть переданы в switch в качестве ссылочных аргументов. Это была бы хрупкая, сложная оптимизация.

Если раздувание шаблона является проблемой, я бы серьезно рекомендовал «параноидальную» версию, которая разделяет проблемы шаблона в функцию-обертку. Такие обертки никогда не должны быть очень сложными, так как главное - избежать вздутия!

Если мета-раздувание является другой проблемой (я только что прочитал, что вы используете генератор кода и беспокоитесь о размере исходного кода тысяч таких оболочек для шаблонов), вы можете немного изменить интерфейс:

template< typename T, char (*func)( std::string const & ) >
T get_anything( std::string const &s ) {
    return T( func( s ) );
}

Таким образом, может быть много get_something() функций, и все они могут использоваться как второй аргумент шаблона для get_anything. Вы также можете использовать указатель на член вместо указателя на функцию в качестве параметра шаблона.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...