Оптимизация возвращаемого значения и копирование в C

Некоторые люди не знают, что могут передавать и возвращать структуры по значению в C. Мой вопрос в том, что компилятор делает ненужные копии при возврате структур в компиляторах C. Do C, таких как GCC, используйте оптимизацию оптимизационных значений (RVO) или это концепция С++? Все, что я прочитал о RVO и копировании elision, относится к С++.

Рассмотрим пример. В настоящее время я реализую double-double тип данных в C (или, скорее, float-float для начала, потому что мне легко найти unit test). Рассмотрим следующий код.

typedef struct {
    float hi;
    float lo;
} doublefloat;

doublefloat quick_two_sum(float a, float b) {
    float s = a + b;
    float e = b - (s - a);
    return (doublefloat){s, e};
}

Будет ли компилятор сделать временную копию возвращаемого значения doublefloat или может ли быть временно удалена временная копия?

Как насчет именованной оптимизации возвращаемого значения (NRVO) в C? У меня есть другая функция

doublefloat df64_add(doublefloat a, doublefloat b) {
    doublefloat s, t;
    s = two_sum(a.hi, b.hi);
    t = two_sum(a.lo, b.lo);
    s.lo += t.hi;
    s = quick_two_sum(s.hi, s.lo);
    s.lo += t.lo;
    s = quick_two_sum(s.hi, s.lo);
    return s;
}

В этом случае я возвращаю именованную структуру. Можно ли удалить временную копию в этом случае?

Следует сказать, что это общий вопрос для C и что примеры кода, которые я использовал здесь, являются только примерами (когда я оптимизирую это, я буду использовать SIMD с intrinsics в любом случае). Я знаю, что я мог бы посмотреть на сборку, чтобы увидеть, что делает компилятор, но я думаю, что это интересный вопрос. Тем не менее.

Ответ 1

RVO/NRVO явно разрешены в соответствии с правилом "as-if" в C.

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

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

Ответ 2

Причина, по которой он слишком много охватывал С++, заключается в том, что в С++ RVO имеет побочные эффекты (т.е. не вызывает деструктор временных объектов, а также конструктор копирования или оператор присваивания результирующих объектов).

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

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