Здравствуйте, Erop, Вы писали:
E>Но я хотел бы узнать какие так делают в реале...
Из известных компиляторов так делает clang: пример.
E>Я вполне допускаю, что какие-то компиляторы могут делать такую нестандартную оптимизацию.
Если под нестандартностью ты понимаешь не редкость или сложность подобной оптимизации, а отсутствие её в стандарте, то это не так — явное разрешение не выделять память при вычислении new-expression в стандарте есть.
BZ>>Вообще не понимаю какая вам разница, если видимое поведение не меняется. ничто не мешает занулять v в начадле каждого цикла, например, вместо создания-уничтожения
E>Ну таки вызовы кучи -- это сайд-эффект жеж.
Всё же видимое поведение (observable behavior) и сайд-эффекты (side effects) — это немного разные вещи. В стандарте с самой первой версии было разрешено компилятору игнорировать любые сайд-эффекты, если это не влияет на видимое поведение.
До недавнего времени, правда, в стандарте была неоднозначность: с одной стороны состояние внутренней (не volatile) памяти программы не является напрямую наблюдаемым поведением, а с другой стороны в подавляющем числе реализаций операции по выделению памяти периодически приводят к взаимодействию с окружением или ОС (а формально вызов mmap, sbrk, VirtualAlloc или другой функции ОС — это уже наблюдаемое поведение, или подменённый глобальный аллокатор тоже может что-то сложное и наблюдаемое делать).
К счастью, эта неоднозначность была всё же разрешена в n3664 в пользу первой точки зрения.
В целом правильное изменение, я считаю. Всё равно же большинство программ и программистов пользуются глобальным аллокатором как чёрным ящиком (и даже названия реализации его, зачастую, не знает). Так что тут просто узаконивается уже существующий порядок — поведение аллокатора отделяется от поведения программы — он лишь предоставляет память, а как это делается — не важно. Аналогичное положение, кстати говоря, уже давно существует для других функций, в которых семантика ставится выше реализации. Так, например, компилятор спокойно заменит вызов memcpy(u, v, 4) на копирование 4х байт через регистр. Хотя формально программист может заменить функцию memcpy на свою, которая, например, логирует все вызовы в файл или осуществляет какие-то дополнительные проверки и действия (скажем, подобно valgrind). Но компилятор здесь руководствуется именно семантикой (memcpy копирует память) и игнорирует настоящую реализацию.
Здравствуйте, кубик, Вы писали:
К>Я так понимаю что вектор может сконструируюется в начале скопа, а не по порядку после того как x==2. Так ?
Нет. Не может нарушаться видимое поведение.
Rather, conforming implementations are required to emulate (only) the observable behavior of the abstract machine as explained below.
А оно такое:
Every value computation and side effect associated with a full-expression is sequenced before every value computation and side effect associated with the next full-expression to be evaluated.
Переубедить Вас, к сожалению, мне не удастся, поэтому сразу перейду к оскорблениям.
Здравствуйте, Mr.Delphist, Вы писали:
К>>for (..) { К>> x=2; К>> vector v(x); К>>}
К>>////////////////////////////// К>>Я так понимаю что вектор может сконструируюется в начале скопа, а не по порядку после того как x==2. Так ?
MD>Если x явно присваивается двойке на каждой итерации цикла, то да, может произойти propagation с вынесением из скоупа: MD>
MD>Если же x определяется на каждой итерации цикла в разные значения, то, само собой, вектор останется в теле цикла как локальная сущность.
Это что это за компилятор может так издевнуться, выбросив пересоздание объекта на каждой итерации? Вынести "x = 2" — это одно. Вынести vector v(x) — это саботаж.
Кажется, мы о разных вещах говорим. В этом примере вторая итерация цикла вылетит на assert, и до ввода-вывода не доберётся. С оптимизацией или без, с сайд-эффектами или без, с процессорным реордерингом или без — вылетит. Потому что.
И если вектор был определен внутри цикла, а оптимизатор выдернет его наружу — это саботаж в случаях, если вектор изменяется внутри цикла, и его содержимое хоть как-то используется. А если нет, то оптимизатор просто может выбросить весь цикл, нет нужды его разворачивать.
Здравствуйте, Mr.Delphist, Вы писали:
MD>Если x явно присваивается двойке на каждой итерации цикла, то да, может произойти propagation с вынесением из скоупа: MD>
MD>Если же x определяется на каждой итерации цикла в разные значения, то, само собой, вектор останется в теле цикла как локальная сущность.
А как же деструктор v, который каждую итерацию звать надо?..
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
К>////////////////////////////// К>Я так понимаю что вектор может сконструируюется в начале скопа, а не по порядку после того как x==2. Так ?
Нет, не может в данном случае. Что вероятно с оптимизирующим компилятором — первое присваивание вообще будет забыто, и i инициализируется 2.
Здравствуйте, кубик, Вы писали:
К>Всем привет,
К>Имеем: К>////////////////////////////// К>int x; К>x= 1; К>for (..) { К> x=2; К> vector v(x); К>}
К>////////////////////////////// К>Я так понимаю что вектор может сконструируюется в начале скопа, а не по порядку после того как x==2. Так ?
Если x явно присваивается двойке на каждой итерации цикла, то да, может произойти propagation с вынесением из скоупа:
int x = 2;
vector v(x);
for (...) {
...
}
Если же x определяется на каждой итерации цикла в разные значения, то, само собой, вектор останется в теле цикла как локальная сущность.
Здравствуйте, Skipy Rich, Вы писали:
SR>Это что это за компилятор может так издевнуться, выбросив пересоздание объекта на каждой итерации? Вынести "x = 2" — это одно. Вынести vector v(x) — это саботаж.
Если инвариант соблюден, то это полностью корректная оптимизация. Само собой, что если конструктор имеет сайд-эффекты (лезет в сеть или к файловой системе, например), то всё останется на своих местах с повторяющимся пересозданием. А так — имеем меньшее число тактов (т.к. инстанциируем один раз), ниже чехарда на хипе и т.п. Профит!
Здравствуйте, Mr.Delphist, Вы писали:
MD>Здравствуйте, Skipy Rich, Вы писали:
SR>>Это что это за компилятор может так издевнуться, выбросив пересоздание объекта на каждой итерации? Вынести "x = 2" — это одно. Вынести vector v(x) — это саботаж.
MD>Если инвариант соблюден, то это полностью корректная оптимизация. Само собой, что если конструктор имеет сайд-эффекты (лезет в сеть или к файловой системе, например), то всё останется на своих местах с повторяющимся пересозданием. А так — имеем меньшее число тактов (т.к. инстанциируем один раз), ниже чехарда на хипе и т.п. Профит!
Здравствуйте, Skipy Rich, Вы писали:
SR>Здравствуйте, Mr.Delphist, Вы писали:
MD>>Здравствуйте, Skipy Rich, Вы писали:
SR>>>Это что это за компилятор может так издевнуться, выбросив пересоздание объекта на каждой итерации? Вынести "x = 2" — это одно. Вынести vector v(x) — это саботаж.
MD>>Если инвариант соблюден, то это полностью корректная оптимизация. Само собой, что если конструктор имеет сайд-эффекты (лезет в сеть или к файловой системе, например), то всё останется на своих местах с повторяющимся пересозданием. А так — имеем меньшее число тактов (т.к. инстанциируем один раз), ниже чехарда на хипе и т.п. Профит!
SR>
По счастью, времена "измерителей производительности" из разряда "засекаем время, крутим миллион пустых итераций, сравниваем время и вычисляем индекс производительности" давно ушли в прошлое. Скажем, Ваш цикл может быть линеаризован, ввиду малого диапазона j, далее выбрасываются дубликаты (помните сообщения от компилятора "Assignment result is not used"? вот тут они тоже сработают), и получаем совсем другой код, но выдающий ту же самую бизнес-логику. К пуговицам претензии есть? (с)
Компилятор поумнел. Вы действительно считаете, что он, особенно в релизную сборку, будет компилировать всё как есть, без собственных мыслей по этому поводу? Так я Вам даже больше скажу — процессор тоже в этом плане давно не дурак, и может позволять себе вольности с порядком исполнения скомпилированных инструкций, как будет удобнее для загрузки конвейера (CPU reordering).
Да даже дебажные сборки иногда "радуют" фактом пусть неагрессивной, но всё же оптимизации, из-за чего окошко Watches будет показывать печальный warning вместо реального значения.
Здравствуйте, Erop, Вы писали:
E>Здравствуйте, Mr.Delphist, Вы писали:
MD>>Если x явно присваивается двойке на каждой итерации цикла, то да, может произойти propagation с вынесением из скоупа: MD>>
MD>>Если же x определяется на каждой итерации цикла в разные значения, то, само собой, вектор останется в теле цикла как локальная сущность.
E>А как же деструктор v, который каждую итерацию звать надо?..
Дык ну батенька, мы же с Вами взрослые люди. Если конструктор, деструктор и прочие должные к вызову вещи не имеют сайд-эффектов и для компилятора выглядят как дублирование одного и того же действия с игнорированием результата — то здравствуй propagation с вынесением из скоупа. А ежели сам вектор v вообще не используется в коде, то и propagation никакого не состоится — "...весь мир насилья мы разрушим до основанья, а затем...".
MD>Если конструктор, деструктор и прочие должные к вызову вещи не имеют сайд-эффектов и для компилятора выглядят как дублирование одного и того же действия с игнорированием результата — то здравствуй propagation с вынесением из скоупа. А ежели сам вектор v вообще не используется в коде, то и propagation никакого не состоится — "...весь мир насилья мы разрушим до основанья, а затем...".
А сама по себе аллкация разве не сайд-эффект? Я же могу ПТОМ перекрыть operator new, например?
А у нас раздельная компиляция там, все дела?
Конечно сейчас это все компиляторы читят, тут не спорю, но что бы вот так? За исключением некоторых явно перечисленных в стандарте случаев вроде RVO и NRVO, вроде как это несовместимая оптимизация будет?
MD>Дык ну батенька, мы же с Вами взрослые люди.
\
Вот именно. Какие конкретно компиляторы так умеют?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Erop, Вы писали:
E>А сама по себе аллкация разве не сайд-эффект? Я же могу ПТОМ перекрыть operator new, например? E>А у нас раздельная компиляция там, все дела?
с LTO? вообще не понимаю какая вам разница, если видимое поведение не меняется. ничто не мешает занулять v в начадле каждого цикла, например, вместо создания-уничтожения
Здравствуйте, BulatZiganshin, Вы писали:
BZ>Вообще не понимаю какая вам разница, если видимое поведение не меняется. ничто не мешает занулять v в начадле каждого цикла, например, вместо создания-уничтожения
Ну таки вызовы кучи -- это сайд-эффект жеж.
Я вполне допускаю, что какие-то компиляторы могут делать такую нестандартную оптимизацию. Но я хотел бы узнать какие так делают в реале...
Я кстати писал это всё в сообщении, на которое ты отвечаешь
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, watchmaker, Вы писали:
W>...отсутствие её в стандарте, то это не так — явное разрешение не выделять память при вычислении new-expression в стандарте есть.
А разве vector<int> v(15); — это new-expression?
W>К счастью, эта неоднозначность была всё же разрешена в n3664 в пользу первой точки зрения.
Спасибо, не знал. Ну и вообще есть много всяких разных отступлений в похожих вопросах, ради удобства оптимизатора.
Скажем то, что можно опускать конструктор копии в конструкциях вроде
std::string s = std::string( "!!!" );
Даже если у него есть и видимое поведение и сайд-эффекты...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Вышеприведённый ответ был не столько про vector, сколько комментарием к этой строчке: E>>>Ну таки вызовы кучи -- это сайд-эффект жеж.
W>>...отсутствие её в стандарте, то это не так — явное разрешение не выделять память при вычислении new-expression в стандарте есть.
E>А разве vector<int> v(15); — это new-expression?
Нет. Да с аллокатором по умолчанию внутри vector<int> память выделяется прямым вызовом ::operator new, а не опосредованно через new-expression. Что опять же формально разные вещи. Впрочем для умолчательного аллокатора также есть отдельная оговорка, что не каждый вызов allocate обязан приводить к вызову ::operator new.
Здравствуйте, watchmaker, Вы писали:
W>Нет. Да с аллокатором по умолчанию внутри vector<int> память выделяется прямым вызовом ::operator new, а не опосредованно через new-expression. Что опять же формально разные вещи. Впрочем для умолчательного аллокатора также есть отдельная оговорка, что не каждый вызов allocate обязан приводить к вызову ::operator new.
Ergo: стандартность такой оптимизации зависит от реализации STL, то есть зависит это неспецифицированное поведение?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Mr.Delphist, Вы писали:
MD>... Если конструктор, деструктор и прочие должные к вызову вещи не имеют сайд-эффектов
А что такое сайд-эффект в данном случае? Запись значения в глобальную переменную в конструкторе — это сайд-эффект?
Вызов из конструктора функции определенной в другой единице трансляции — это сайд-эффект?
MD>и для компилятора выглядят как дублирование одного и того же действия с игнорированием результата
"Выглядят" закончится там, где компилятор обнаружит вызов функции определенной в другой единице трансляции.
MD> — то здравствуй propagation с вынесением из скоупа.
В данном случае propagation называется invariant code motion и компилятор его скорее всего не сделает.
Даже вынос присваивания x=2 можно делать только если цикл исполняется хотя бы один раз, а это не всегда можно проверить.
MD>А ежели сам вектор v вообще не используется в коде,
А ежели цикл не исполняется ни разу, то ваш код не эквивалентен тому, что был у ТС.