Warning: Cannot use a scalar value as an array in /home/admin/public_html/forum/include/fm.class.php on line 757
Warning: Invalid argument supplied for foreach() in /home/admin/public_html/forum/include/fm.class.php on line 770
Warning: Invalid argument supplied for foreach() in /home/admin/public_html/forum/topic.php on line 737 Форумы портала PHP.SU :: Вопрос к программистам [9]
Покинул форум
Сообщений всего: 1459
Дата рег-ции: Авг. 2008 Откуда: Крым
Помог: 11 раз(а)
LIME, тогда поправлюсь: "Очередь выполнения".
LIME
Отправлено: 26 Сентября, 2013 - 10:08:33
Активный участник
Покинул форум
Сообщений всего: 10732
Дата рег-ции: Нояб. 2010
Помог: 322 раз(а)
ALEN а в чем состоит поправка?
EuGen
Отправлено: 26 Сентября, 2013 - 10:10:20
Профессионал
Покинул форум
Сообщений всего: 9095
Дата рег-ции: Июнь 2007 Откуда: Berlin
Помог: 707 раз(а)
Итак, чтобы более не затягивать ответ - опишу ситуацию как можно подробнее - насколько смогу. Прежде всего, прошу прощения за некий вызов, сделанный мною сообществу в hint1 - это было сделано не с целью выразить мысль "всё равно не ответите", а с целью подогреть интерес. Надеюсь, ни один пользователь не в обиде.
Итоги и направление мыслей
На самом деле, полный ответ на поставленный вопрос уходит далеко за рамки обычного использования PHP. Скажу, что наиболее правильные мысли были выражены Ильёй (DeepVarvar) и caballero (к сожалению, не знаю имени). Илья предположил, что дело в том, как интерпретатор работает с такими выражениями на уровне байткода (п. 2 в комментарии), тогда как caballero разумно предложил посмотреть, что происходит на ассемблерном уровне. На самом деле, ассемблерный уровень показал бы, что это действительно так происходит, но не показал бы, почему это так - поскольку это уже скомпилированные из опкодов готовые процессорные инструкции. Правда кроется как раз в опкодах, которые получаются после интерпретации программы и которые затем исполняются в Zend VM.
Ассоциативность и приоритет
Многие предполагали, что суть дела состоит в приоритете операторов или в некоторой ассоциативности. Отчасти это правда, однако без понимания, как именно это работает, ответ всё равно не очевиден. Правда состоит в том, что на самом деле выражения в PHP не выполняются с учётом ассоциативности и приоритета. Ассоциативность и приоритет лишь показывают, как выражения будут сгруппированы - но не порядок их дальнейшего исполнения. Почувствуйте разницу. Для обсуждаемых примеров:
// "++" имеет более высокий приоритет, чем "+", поэтому "$x++" будет сгруппирован, что равносильно
$x+($x++);
//во втором выражении:
$x+$x+$x++;
// и снова "++" имеет более высокий приоритет, чем "+", так что группировка будет такой:
$x+$x+($x++);
// и, наконец, "+" - это лево-ассоциативный оператор, поэтому левый "+" будет сгруппирован (но не правый!):
($x+$x)+($x++);
Понятнее? Наверно. Однако - что это значит в плане порядка вычисления выражения? А ничего. В последнем выражении левая часть ($x + $x) или же правая ($x++) - могут вычисляться первыми. И PHP не регламентирует, что же случится в таком случае на самом деле. Поэтому вывод таков - не стоит полагаться на это (то есть ошибочно считать, что ассоциативность и приоритет определяют порядок выполнения выражения) в своих программах. Однако же это не даёт полного ответа, верно? И тем более про случай с "@".
Оптимизация "компилированных переменных"
Если предыдущая часть ещё могла быть предположена (хотя ни из одного руководства это явно не следует), то эта - уже намного более специфична - и именно здесь скрыт ответ, почему так происходит в обсуждаемых примерах.
Немного пояснений. PHP исполняется в два этапа. На первом этапе интерпретатор проходит программу и генерирует опкоды - некоторые инструкции, которые имеют два операнда и инструкцию, к которой эти операнды применяются. Затем опкоды исполняются в Zend VM (Zend Virtual Machine) для получения уже готовых машинных инструкций.
Начиная с версии 5.1 в PHP введена функциональность оптимизации "компилированных переменных" (compiled variables optimization). Эта оптимизация позволяет простым переменным быть непосредственно операндами самих опкодов. Простые переменные - это, например, $foo или $bar - но не $foo->bar или $foo['bar']. Итак, можно проверить, что же получится в результате обработки первого примера в терминах опкодов:
- в принципе, должно быть интуитивно понятно соответствие опкодов операциям программы. Обратите внимание, что $x может участвовать в опкодах непосредственно - так как она является простой переменной. На всякий случай, всё же поясню: сначала присвоить $x значение 1 (ASSIGN $x, 1), затем добавить к $x её же и сохранить результат в $tmp_1 ($tmp_1 = ADD $x, $x), затем выполнить постинкремент для $x и сохранить результат в $tmp_2 ($tmp_2 = POST_INC $x), затем сложить $tmp1 и $tmp2 с сохранением результата в $tmp_3 ($tmp_3 = ADD $tmp_1, $tmp_2) - и, наконец, присвоить $x конечное значение выражения (ASSIGN $y, $tmp_3). Как видно, в данном примере вычисление произошло слева направо (вероятно, ожидаемый порядок)
Теперь первый пример.
Опкоды, которые получатся в результате, будут такими:
- как видно, в данном случае постинкремент выполнился сначала, значение же $x было прочитано только после опкода ADD. Почему так? Потому что чтение значения $x не требует дополнительного опкода. Любой опкод может обработать операцию "прочтения" значения переменной в силу "оптимизации компилированных переменных". В этом и есть её суть - то есть, так как переменная указывает на своё же значение, то такого опкода для чтения и не требуется (в этом и состоит оптимизация). Таким образом, опкод одновременно начинает читать значение и, собственно, выполняться.
Как этого избежать?
Существуют некоторые весьма редкие случаи, когда оптимизация компилированных переменных не выполняется. К таким случаям относится использование оператора "@". Разберу пример с его использованием:
- как видно, применение этого оператора существенно поменяло картину. Теперь всё заключено внутри опкодов BEGIN_SILENCE/END_SILENCE - однако это не главное. Главное - в том, что теперь, вместо описанной выше оптимизации, PHP применил опкоды FETCH_R (получение для чтения) и FETCH_W (получение для записи) к $x и $y - вместо того, чтобы эти самые $x и $y использовать в качестве операндов для опкодов. И в этом и состоит вся суть - теперь значение будет получено до применения опкода сложения, тогда как в случае оптимизации этого не происходило. А сделано до сложения это будет, разумеется потому, что $var_3 = FETCH_R 'x' теперь самый настоящий, "полноценный" опкод и порядок для него уже имеет значение.
Резюме
Откуда это всё взялось? Примеры появились, конечно, искусственно, но в ходе достаточно оживлённой дискуссии, в которой мне посчастливилось поучаствовать. Это - не то, что можно прочесть из руководств. Строго говоря, это вообще нигде нельзя прочесть, потому что многое было получено непосредственно из чтения исходных кодов интерпретатора и опкодов для VM. В нормальной ситуации такой проблемы не возникнет, так как опытный разработчик, в случае неуверенности, просто заключит части выражения в скобки. Какие выводы? Думаю, следующие:
- Не стоит полагаться на порядок выполнения выражения. Строго говоря, в общем случае он не регламентирован. Если есть сомнения - используйте скобки.
- Оператор "@" нарушает оптимизацию внутри опкодов. По сути, он замедляет исполнение программы, так что лучше его не использовать - и, как видите, не только потому, что он подавляет ошибки.
P.S. не помещаю ответ в спойлер, так как все участники согласились с его необходимостью.
----- Есть в мире две бесконечные вещи - это Вселенная и человеческая глупость. Но насчет первой .. я не уверен.
LIME
Отправлено: 26 Сентября, 2013 - 10:17:05
Активный участник
Покинул форум
Сообщений всего: 10732
Дата рег-ции: Нояб. 2010
Помог: 322 раз(а)
EuGen спасибо...весьма познавательно
SAD
Отправлено: 26 Сентября, 2013 - 10:17:16
Постоянный участник
Покинул форум
Сообщений всего: 2508
Дата рег-ции: Май 2009 Откуда: Днепропетровск, Украина
Помог: 75 раз(а)
Мой последний пост был как раз про это Ассоциативность и приоритет =)
Покинул форум
Сообщений всего: 5998
Дата рег-ции: Сент. 2011 Откуда: Харьков
Помог: 126 раз(а)
Цитата:
Если есть сомнения - используйте скобки.
я всегда так делаю в сомнительных случаях, от греха подальше.
Цитата:
наиболее правильные мысли были выражены Ильёй (DeepVarvar) и caballero (к сожалению, не знаю имени).
Леонид, если кому интересно
Вообще это логично, те кто работают с С и С++ всегда пытаются смотреть на уровень машинного кода.
Я правда упустил что опкод компилится ассемблерным компилятором у которого свои правила оптимизации и он о PHP ничего не знает.
Покинул форум
Сообщений всего: 9095
Дата рег-ции: Июнь 2007 Откуда: Berlin
Помог: 707 раз(а)
Перенесено из темы "Вопрос к программистам" SAD
Суть как раз в том, что это здесь никак не решает вопрос. То есть не даёт на него ответ.
Всем участникам - прошу прощения. Введена некоторая настройка конференции, ограничивающая число сообщений в теме, после чего создётся её "продолжение". Зачем это нужно - я не знаю. И я не смог пока что договориться об этом с владельцами сервера, но это очень мешает.
----- Есть в мире две бесконечные вещи - это Вселенная и человеческая глупость. Но насчет первой .. я не уверен.
imya
Отправлено: 26 Сентября, 2013 - 10:36:14
Участник
Покинул форум
Сообщений всего: 1472
Дата рег-ции: Сент. 2012 Откуда: Запорожье, Украина
Помог: 19 раз(а)
Перенесено из темы "Вопрос к программистам" Оу...действительно, ответ кроется значительно глубже...почитаю на досуге, а то работой завалили...
Все гости форума могут просматривать этот раздел. Только зарегистрированные пользователи могут создавать новые темы в этом разделе. Только зарегистрированные пользователи могут отвечать на сообщения в этом разделе.