dimanche 6 août 2023

Noexcept - Ce n'est pas gratuit !

Je parle régulièrement de culte du cargo dans le domaine informatique, où l'on a souvent ses petites marottes que l'on applique sans comprendre. Et j'ai un chef dont la marotte c'est d'exiger l'ajout du mot-clé "noexcept" un peu partout, avec l'espoir d'augmenter les performances.

Et c'est le cas, parfois. Par exemple, une réallocation d'un std::vector après un dépassement de capacité lors de l'ajout d'un élément sera bien plus efficace si le type de l'élément a un move constructor designé comme noexcept, car dans ce cas, l'algorithme de réallocation du vecteur peut utiliser le move. Il faut donc bien se rendre compte qu'au sein du code de std::vector, il y a une condition qui vérifie à la compilation si le type a son move constructor designé ainsi, et décide d'utiliser l'algo efficace ou non. Ce n'est donc pas une optimisation du compilateur, mais bien un choix de code au sein de la bibliothèque standard, et c'est complètement indépendant du niveau d'optimisation, de l'inlining...

Et surtout, noexcept n'est pas gratuit ! Si le compilo ne peut déterminer si effectivement la fonction marquée noexcept ne lance pas d'exception (disons, elle appelle simplement une autre fonction pas inlinée qui n'est pas déclarée noexcept), il lui faut rajouter du code pour pouvoir attraper cette exception et terminer le programme à la place. Voyons ce que dit notre ami Godbolt sur ce tout petit programme:

void f();

void g()
{
    f();
}

L'assembleur généré est celui-là, c'est à dire un bête jump et puis c'est tout !

g():
        jmp     f()

En revanche, si l'on dit maintenant que g() est noexcept:

void f();

void g() noexcept
{
    f();
}

Alors l'assembleur généré nous montre bien que l'on fait un peu plus de travail, en poussant la frame sur la pile, afin que si une exception était lancée dans f(), il soit possible de l'arrêter dans g().

g():
        subq    $8, %rsp
        call    f()
        addq    $8, %rsp
        ret

Conclusion que ne renieraient pas Plic et Ploc : réfléchir avant d'agir !