12 votes

Pourquoi les compilateurs n'optimisent-ils pas cela ?

Regardez ce code :

struct Data {
};

struct Init {
    Data *m_data;

    Init() : m_data(new Data) { }
    ~Init() {
        delete m_data;
    }
};

class Object {
    private:
        const int m_initType;
        Data *m_data;
    public:
        Object(const Init &init) : m_initType(0), m_data(init.m_data) { }
        Object(Init &&init) : m_initType(1), m_data(init.m_data) { init.m_data = nullptr; }
        ~Object() {
            if (m_initType==1) {
                delete m_data;
            }
        }
};

void somefunction(const Object &object); // it is intentionally not defined

void callInitA() {
        Init x;
        somefunction(x);
}

void callInitB() {
        somefunction(Init());
}

Comme Object::m_initType est const, elle ne change pas après le constructeur. Donc, en théorie, dans callInitA et dans callInitB le compilateur connaît la valeur de m_initType lorsqu'il met en ligne ~Object() . Cependant, gcc et clang ne s'applique pas cette optimisation, et vérifie à la fois la valeur de m_initType .

Pourquoi ça ? Existe-t-il une règle linguistique interdisant cette optimisation, ou les compilateurs ne font tout simplement pas ce genre d'optimisation ?

(Cette question est étroitement liée à este mais il s'agit d'une question plus spécifique, j'espère pouvoir obtenir une réponse à ce sujet).

3voto

Passer By Points 9171

Pour répondre à la question de savoir s'il y a des règles dans le langage qui interdisent ce type d'optimisation, voici mon point de vue

De [dcl.type.cv]

Sauf que tout membre de classe déclaré mutable peut être modifié, toute tentative de modification d'un objet const pendant sa durée de vie entraîne un comportement non défini.

Et donc, en théorie, l'optimiseur peut supposer en toute sécurité m_initType ne changera jamais après l'initialisation. Ceci peut bien sûr être utilisé pour déduire si la branche de ~Object sera prise au moment de la compilation.

Cela dit, les optimiseurs sont libres de faire n'importe quoi tant que le comportement observé reste le même, ils sont également libres d'ignorer const . Pour compliquer encore les choses pour l'optimiseur, il y a une fonction déclarée mais non définie dans le mélange, l'optimiseur a probablement abandonné après cela pour faire quelque chose d'utile avec l'information.

Comparaison entre une fonction définie et une fonction non définie

Si la fonction est définie plus tard, gcc et clang optimisent tout. Notez cependant que dans ce cas particulier, ils vont toujours faire cela même sans const .

Ce poste pourrait vous intéresser

0voto

Noname Points 53

Le destructeur d'objet n'est pas inlined dans votre exemple et vous avez 2 invocations, où dans un m_initType est 1 et dans l'autre est 0. Donc le compilateur doit supporter les deux versions. De plus, je suppose que votre code réel est un peu plus complexe que votre exemple, donc le compilateur pourrait décider que l'inline de tout le code du destructeur est plus coûteux que de garder la version générique avec un seul 'if' à l'intérieur.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X