Сколько стоит раздутый объектный файл? - PullRequest
0 голосов
/ 21 ноября 2018

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

Позвольте мне проиллюстрировать это на примере.

Исходные файлы 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 операторов.

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

1 Ответ

0 голосов
/ 21 ноября 2018

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

Этот видвещь может быть ОЧЕНЬ трудно предсказать, все же.Способ найти лучшее место в этом (и любом) типе оптимизации состоит в измерении.Он также может меняться на разных платформах, особенно во встроенном мире.

...