Работая над встроенным проектом, я столкнулся с функцией, которая вызывается тысячи раз при жизни приложения, часто в циклах, десятки раз в секунду.Я задавался вопросом, смогу ли я уменьшить его стоимость, и я обнаружил, что большинство его параметров известны во время компиляции.
Позвольте мне проиллюстрировать это на примере.
Исходные файлы hpp / cpp можно приблизительно аппроксимировать следующим образом:
original.hpp:
void example(bool arg1, bool arg2, const char* data);
original.cpp:
#include "ex1.hpp"
#include <iostream>
void example(bool arg1, bool arg2, const char* data)
{
if (arg1 && arg2)
{
std::cout << "Both true " << data << std::endl;
}
else if (!arg1 && arg2)
{
std::cout << "False and true " << data << std::endl;
}
else if (arg1 && !arg2)
{
std::cout << "True and false " << data << std::endl;
}
else
{
std::cout << "Both false " << data << std::endl;
}
}
Предположим, что каждый раз, когда вызывается функция, arg1
и arg2
известны во время компиляции.Аргумент data
не является, и по разным причинам его обработка не может быть помещена в заголовочный файл.
Однако все эти операторы if
могут быть обработаны компилятором с небольшим количеством магии шаблона:
magic.hpp:
template<bool arg1, bool arg2>
void example(const char* data);
magic.cpp:
#include "ex1.hpp"
#include <iostream>
template<bool arg1, bool arg2>
struct Processor;
template<>
struct Processor<true, true>
{
static void process(const char* data)
{
std::cout << "Both true " << data << std::endl;
}
};
template<>
struct Processor<false, true>
{
static void process(const char* data)
{
std::cout << "False and true " << data << std::endl;
}
};
template<>
struct Processor<true, false>
{
static void process(const char* data)
{
std::cout << "True and false " << data << std::endl;
}
};
template<>
struct Processor<false, false>
{
static void process(const char* data)
{
std::cout << "Both false " << data << std::endl;
}
};
template<bool arg1, bool arg2>
void example(const char* data)
{
Processor<arg1, arg2>::process(data);
}
template void example<true, true>(const char*);
template void example<false, true>(const char*);
template void example<true, false>(const char*);
template void example<false, false>(const char*);
Как видите, даже на этом крошечном примере файл cpp стал значительно больше по сравнению соригинал.Но я удалил несколько инструкций на ассемблере!
Теперь в моем случае все немного сложнее, потому что вместо двух bool
аргументов у меня есть перечисления и структуры.Короче говоря, все комбинации дают мне около тысячи комбинаций, поэтому у меня так много экземпляров строки template void example<something>(const char*);
Конечно, я не генерирую их вручную, но с помощью макросов, но все же файл cpp становится огромным,по сравнению с исходным и объектным файлом еще хуже.
Все это во имя удаления нескольких if
и одного switch
операторов.
Мой вопрос: является ли размер единственной проблемой при использовании шаблона-магии?Интересно, есть ли какая-то скрытая стоимость с использованием стольких версий одной и той же функции.Я действительно сэкономил некоторые ресурсы, или только наоборот?