Как я могу заставить компилятор сгенерированного компилятором класса * не * быть встроенным компилятором? - PullRequest
7 голосов
/ 06 октября 2010

Альтернативный заголовок вопроса будет: Как явно заставить компилятор генерировать код для сгенерированных компилятором конструкторов в определенной единице перевода?

Проблема, с которой мы сталкиваемся, заключается в том, что для одного кодапуть к полученной - тщательно измеренной - производительности лучше (примерно на 5%), если вызовы copy-ctor одного объекта не встроены, то есть если этот конструктор реализованвручную.(Мы заметили это, потому что во время очистки кода была удалена лишняя явно реализованная копия ctor этого класса (17 членов).)

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

Теперь перед нами стоит выбор - просто сбросить код copy-ctor вручную (он делает то же самое, что и сгенерированный компилятором) или найти другие способы , а не , вставляющие копиюctor этого класса.

Существуют ли какие-либо средства (для Microsoft Visual C ++) для явного создания экземпляров функций классов, сгенерированных компилятором, в конкретной единице перевода или они всегда будут встроены в каждую единицу перевода, где они используются?(Комментарии к gcc или другим компиляторам также позволяют получить более полное представление о ситуации.)


Поскольку первые 2 ответа показывают некоторое недопонимание: компилятор сгенерировал функции классагенерируется только самим компилятором, если они не объявлены и не определены пользователем.Поэтому никакие модификаторы не могут быть применены к ним, так как эти функции не существуют в исходном коде.

struct A {
  std::string member;
};

A имеет значение по умолчанию и копию ctor, dtor и копиюоператор.Ни одна из этих функций не может быть изменена с помощью некоторого declspec, поскольку они не существуют в коде.

struct B {
  std::string member;
  B(B const& rhs);
};

B теперь имеет предоставленный пользователем copy ctor, и пользователь должен реализоватьЭто.Компилятор не будет генерировать для него код.


Еще немного предыстории для сомневающихся :-) ...

Этот код скомпилирован с использованием MS Visual C ++, но он связан длявстроенная (-подобная) (в реальном времени) система.Производительность измерялась с помощью измерения времени в этой системе, и поэтому я думаю, что у парней, которые взяли время, будут приличные числа.

Тест проводился путем сравнения двух версий кода, в которых разница была только .был встроенный и не встроенный экземпляр ctor этого одного класса.Времена с встроенным кодом были хуже примерно на 5%.


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

Ответы [ 6 ]

5 голосов
/ 06 октября 2010

$ 12,1 / 5- "Неявно объявленный конструктор по умолчанию является встроенным открытый член своего класса. ".

Так что мы ничего не можем сделать. Конструктор implcit должен быть встроенным. Любое другое поведение в этом отношении, вероятно, будет расширением

Сказав это,

Вероятно, ваш ручной конструктор копирования (который вы удалили во время очистки кода) делал все правильно. Например, если один из членов (из 17) вашего класса является членом-указателем, вполне вероятно, что конструктор ручного копирования позаботился о глубоком копировании (и, следовательно, получил снижение производительности).

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

3 голосов
/ 06 октября 2010

Я очень сомневаюсь, что встраивание связано с этим.Если компилятор вставляет ctor-сгенерированный компилятором ctor, почему бы ему не встроить явно определенный?(Также необычно, что эвристика оптимизации компилятора терпит неудачу так сильно, что делает встроенный код на 5% медленнее)

Прежде чем делать выводы,

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

Если это так, не могли бы вы обновить свой вопрос этой информацией?

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

В C ++ 0x это может быть возможным (в зависимости от того, как эти специфичные для поставщика расширения взаимодействуют с функциями, объявленными как = default).

НоОпять же, я не уверен, что проблема заключается во вложении.Скорее всего, две функции просто приводят к генерированию разного кода сборки.

0 голосов
/ 05 ноября 2010

Чтобы добавить свой вывод и ответить на точный вопрос, не вдаваясь в детали:

  1. Вы не можете заставить компилятор, особенно VC ++, встроить или не встроить сгенерированный компилятором ctor / dtor / etc. - но

  2. Оптимизатор выберет - по своему усмотрению - будет ли он включать код для функции, сгенерированной компилятором (ctor), или если он генерирует «реальную» функцию для этого кода. AFAIK нет никакого способа повлиять на решение оптимизатора в этом отношении.

0 голосов
/ 06 октября 2010

Вы можете использовать какой-то вложенный объект.Таким образом, конструктор копирования вложенного объекта можно оставить в качестве необслуживаемого по умолчанию, но у вас все еще есть явно созданный конструктор копирования, который можно объявить noinline.

class some_object_wrapper {
    original_object obj;
    __declspec(noinline) some_object_wrapper(const some_object_wrapper& ref) 
        : obj(ref) {}
    // Other function accesses and such here
};

Если вы в отчаянии, вы можетескомпилируйте данный класс отдельно в .lib и дайте ссылку на него.Изменение его на другой модуль перевода не помешает VC ++ встроить его.Кроме того, я должен спросить, действительно ли они делают то же самое.Почему вы реализовали ручной конструктор копирования, если он делает то же самое, что и конструктор копирования по умолчанию?

0 голосов
/ 06 октября 2010

часто лучше выделить его из нескольких основных типов, которые, как вы знаете, проблематичны. пример а:

class t_std_string {
    std::string d_string;
public:
    /* ... */

    /* defined explicitly, and out of line -- you know what to do here */
    t_std_string();
    t_std_string(const std::string& other);
    t_std_string(const t_std_string& other);
    ~t_std_string();

    inline std::string& get() { return this->d_string; }
    inline const std::string& get() const { return this->d_string; }
    /* ... */
};

struct B {
    t_std_string member;
    /* 16 more */
    /* ... */
};

или вы можете взять его бесплатно. пример б:

/* B.hpp */

struct B {
private:

    /* class types */
    struct t_data {
        std::string member;

        /* 16 more ... */
    public:
        /* declare + implement the ctor B needs */

        /* since it is otherwise inaccessible, it will only hurt build times to make default ctor/dtor implicit (or by implementing them in the header, of course), so define these explicitly in the cpp file */
        t_data();
        ~t_data();

        /* allow implicit copy ctor and assign -- this could hurt your build times, however. it depends on the complexity/visibility of the implementation of the data and the number of TUs in which this interface is visible. since only one object needs this... it's wasteful in large systems */
    };
private:

    /* class data */
    t_data d_data;
public:
    /* you'll often want the next 4 out of line
       -- it depends on how this is created/copied/destroyed in the wild
     */
    B();
    B(const B& other);
    ~B();
    B& operator=(const B&);
};

/* B.cpp */

/* assuming these have been implemented properly for t_data */
B::B() : d_data() {
}

B::B(const B& other) : d_data(other) {
}

B::~B() {
}

B& B::operator=(const B&) {
    /* assuming the default behaviour is correct...*/
    this->d_data = other.d_data;
    return *this;
}
/* continue to B::t_data definitions */
0 голосов
/ 06 октября 2010

__ declspec (noinline) .

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

...