Комментарии 8
А на VN ведь делается проверка на hash collision? Или удаётся построить идеальный хэш?
В случае коллизии проверяется на эквивалентность. В зависимости от затейливости имплементации, она может банально сравнивать опкод + аргументы и полагаться на каноническую форму, либо кривляться на тему сложных проверок эквивалентности. LLVM следует первому подходу (https://github.com/llvm/llvm-project/blob/main/llvm/lib/Transforms/Scalar/GVN.cpp#L147).
В целом, реально существующие имплементации реализуют достаточно простой вариант, без [сложного] символьного анализа.
сумму
(a + b + c + d)
можно записать также как(d + a + c + b)
В рамках школьной математики можно.
А вот при использовании базовых типов данных (int, double и т.д.) нельзя, ибо они неэквивалентны.
Для целых чисел - можно, сложение ассоциативно и коммутативно. Для плавающей точки, да, нельзя, но для целых чисел - можно. Ну, если сами a, b, c, d
- числа, а не выражения с побочными эффектами, разумеется.
В любых целых типах можно, даже если переполнения работают на кольце. В дробных - тут действительно может получиться другой результат, потому что будут другие округления, но если вы компилируете без StrictFP, то всё равно можно.
Новый код содержит всего 8 операций сложения и умножения вместо 13,
Я что-то пропустил или что-то не понял? Почему нельзя было посчитать в виде:
а = х+у
sum1 = a * a;
a= x- y
sum2 = a * a;
Получается вроде как всего 4 (четыре!) операции.
Потом, в чем польза рефакторинга который из ТРЕХ строчек кода делает десять, причем позволяет растащить эти строчки хоть в разные файлы, так что потом чтобы проверить арифметику вам придется ползать по всему проекту. Это конечно круто что сложные (как бы) арифметические операции теперь имеют название, но это не значит что анализируя код вам не придется влазить в эти функции чтобы убедиться что там нет опечатки, например, и как же это улучшает читаемость.
Я что-то пропустил или что-то не понял? Почему нельзя было посчитать в виде:
Пропустили.
Данный пример можно было бы оптимизировать и иначе, заметив, что обе суммы -- это знакомые нам со школы формулы квадрата суммы и квадрата разности, но об этом классе оптимизаций мы поговорим потом.
Так можно, но речь не об этом. Этим примером я хотел продемонстрировать совсем другую оптимизацию. Это нормально, что один и тот же код можно оптимизировать разными способами, и обычно компилятор работает по принципу "какая оптимизация первая туда пришла, так и работает".
Потом, в чем польза рефакторинга который из ТРЕХ строчек кода делает десять, причем позволяет растащить эти строчки хоть в разные файлы, так что потом чтобы проверить арифметику вам придется ползать по всему проекту.
В том, что этот пример приводится для того, чтобы объяснить работу данной компиляторной оптимизации, а не в том, чтобы научить вас правильно рефакторить код и организовывать проект. Я это уже объяснял в самой первой статье про SSA-форму:
Просто всякий раз, когда нам нужно было переприсвоить переменную (чего мы теперь не можем делать) мы должны определить новую переменную и в дальнейшем пользоваться ей. С точки зрения С++, выглядит довольно многословно и бредово, и ни один человек в здравом уме так не будет писать. Но ведь мы стараемся не для человека, а для компилятора!
Поговорим об оптимизирующих компиляторах. Сказ пятый: удаление общих подвыражений