Почему передача структуры по ссылке не является обычной оптимизацией? - PullRequest
24 голосов
/ 16 февраля 2009

До сегодняшнего дня я всегда думал, что приличные компиляторы автоматически преобразуют структурную передачу по значению в передачу по ссылке, если структура достаточно велика, чтобы последняя была быстрее. Насколько я знаю, это кажется легкой задачей. Однако, чтобы удовлетворить мое любопытство относительно того, происходит ли это на самом деле, я создал простой тестовый пример на C ++ и D и посмотрел на выходные данные как GCC, так и Digital Mars D. Оба настаивали на передаче 32-байтовых данных. структуры по значению, когда вся рассматриваемая функция складывала члены и возвращала значения без изменения переданной структуры. Версия C ++ приведена ниже.

#include "iostream.h"

struct S {
    int i, j, k, l, m, n, o, p;
};

int foo(S s) {
    return s.i + s.j + s.k + s.l + s.m + s.n + s.o + s.p;
}

int main() {
    S s;
    int bar = foo(s);
    cout << bar;
}

У меня такой вопрос: почему, черт возьми, компилятор не может оптимизировать что-то подобное для передачи по ссылке вместо того, чтобы фактически помещать все эти int в стек?

Примечание: Используемые переключатели компилятора: GCC -O2 (-O3 встроенный foo ().), DMD -O -inline -релиз.

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

Ответы [ 12 ]

1 голос
/ 16 февраля 2009

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

double x; // using non structs, oh-well

void Foo(double d)
{
      x += d; // ok
      x += d; // Oops
}

void main()
{
     x = 1;
     Foo(x);
}
1 голос
/ 16 февраля 2009

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

Ваш компилятор должен обнаружить: а) что foo не изменяет структуру; b) что foo не выполняет никаких вычислений относительно физического местоположения элементов структуры; И c) что вызывающая сторона или другой поток, созданный вызывающей стороной, не изменяет структуру до завершения работы foo.

В вашем примере, вполне возможно, что компилятор мог бы делать эти вещи, но сохраненная память несущественна и, вероятно, не стоит делать предположения. Что произойдет, если вы запустите ту же программу со структурой, содержащей два миллиона элементов?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...