Не встроенный вызов функции cout.operator<<(int)
является черным ящиком для оптимизатора (поскольку библиотека только что написана на C ++, а оптимизатор видит только прототип; см. Обсуждение в комментариях). Он должен предполагать, что любая память, на которую может указывать глобальная переменная, была изменена.
(Или вызов std::endl
. Кстати, зачем вызывать грипп sh от cout в этой точке вместо просто напечатав '\n'
?)
например, , насколько это известно, std::vector<int> &input
является ссылкой на глобальную переменную, и один из этих вызовов функций изменяет эту глобальную переменную . (Или где-то есть глобальный vector<int> *ptr
, или есть функция, которая возвращает указатель на static vector<int>
в каком-то другом модуле компиляции, или каким-либо другим способом, которым функция может получить ссылку на этот вектор, не передавая ссылку на него нами.
Если бы у вас была локальная переменная, адрес которой никогда не был взят, компилятор мог бы предположить, что вызовы не встроенных функций не могли изменить его. способ для любой глобальной переменной хранить указатель на этот объект. ( Это называется Escape Analysis ). Вот почему компилятор может хранить size_t i
в регистре при вызовах функций. ( int i
можно просто оптимизировать, потому что он затенен size_t i
и не используется иным образом.
Он может сделать то же самое с локальными vector
(то есть для указателей base, end_size и end_capacity.)
ISO C99 имеет решение этой проблемы: int *restrict foo
. Многие компиляторы C ++ поддерживают int *__restrict foo
, обещая, что память, на которую указывает foo
, только , доступ к которой осуществляется через этот указатель. Чаще всего полезно в функциях, которые принимают 2 массива, и вы хотите пообещать компилятору, что они не перекрываются. Таким образом, он может автоматически векторизовать, не генерируя код, чтобы проверить это и запустить запасной вариант l oop.
Комментарии OP:
В Rust неизменяемая ссылка является глобальная гарантия того, что никто не изменяет значение, на которое вы ссылаетесь (эквивалент C ++ restrict
)
Это объясняет, почему Rust может выполнить эту оптимизацию, а C ++ - нет.
Оптимизация вашего C ++
Очевидно, вы должны использовать auto size = input.size();
один раз в верхней части вашей функции, чтобы компилятор знал, что он инвариант al oop. Реализации C ++ не решают эту проблему за вас, поэтому вы должны сделать это самостоятельно.
Вам также может понадобиться const int *data = input.data();
, чтобы также поднять нагрузки указателя данных из std::vector<int>
«блока управления» , К сожалению, оптимизация может потребовать очень неидиоматических c изменений исходного кода.
Rust - гораздо более современный язык, разработанный после того, как разработчики компиляторов узнали, что было возможно на практике для компиляторов. Это действительно показывает и другие способы, в том числе переносное раскрытие некоторых интересных вещей, которые процессоры могут делать через i32.count_ones
, rotate, bit-scan и т. Д. c. Действительно глупо, что ISO C ++ до сих пор не раскрывает ничего из этого, кроме std::bitset::count()
.