Важно знать, что вы спрашиваете здесь. В Go нет обещания, что эта программа будет делать что-то конкретное, потому что она недействительна. Но, как исследование оптимизатора, может быть интересно дать представление о том, как он реализован в настоящее время. Любая программа, которая опиралась на эту информацию, была бы очень fr agile и недействительной, но все же это любопытство.
Мы можем скомпилировать программу, а затем посмотреть на вывод. Мне особенно нравятся две версии, которые вы дали, потому что они позволяют видеть различия. Я выполнил декомпиляцию с использованием Hopper (они скомпилированы с помощью go1.14 darwin / amd64).
Во втором случае программа выглядит так, как вы думаете:
void _main.main.func1(int arg0, int arg1, int arg2, int arg3, int arg4, int arg5, int arg6) {
rax = arg6;
for (rcx = 0x0; rcx < 0x2710; rcx = rcx + 0x1) {
*rax = *rax + 0x1;
}
return;
}
Ничего удивительного здесь нет. Но как насчет первого случая, который вас интересует:
_main.main.func1:
goto _main.main.func1;
Он становится oop. Буквально; вот сборка:
_main.main.func1:
000000000109d1b0 nop ; CODE XREF=_main.main.func1+1
000000000109d1b1 jmp _main.main.func1 ; _main.main.func1
Как это происходит? Что ж, компилятор может посмотреть на этот код:
go func() {
for {
x++
}
}()
И он знает, что ничто никогда не читает x
. x
невозможно что-либо прочитать, потому что вокруг x
нет блокировки, и эта процедура никогда не завершается. Так что нет ничего, что могло бы прочитать x
после , когда эта программа завершена. См. Go Модель памяти для получения дополнительной информации о том, что означает, что что-то должно произойти до чего-то другого или после чего-то другого.
"Но я действительно читаю х!" Нет, ты не Это будет неверный код, и компилятор знает, что вы не написали неверный код. Кто сделал бы это, когда есть детектор гонки, говорящий вам, что это недействительно? Так как компилятор может ясно видеть, что ничего не читается x
, нет причин беспокоиться об его обновлении.
В вашем примере с ограниченным l oop выполнение программы завершается, поэтому вполне возможно, что что-то читает x
после этого. Компилятор не достаточно умен, чтобы заметить, что правильное чтение никогда не выполняется, и поэтому он не оптимизирует это так хорошо, как мог бы. Возможно, будущий компилятор будет достаточно умен, чтобы вывести 0 в обоих случаях. И, возможно, будущий компилятор будет достаточно умен, чтобы полностью удалить вашу неоперативную программу в первом случае.
Но ключевой момент здесь заключается в том, что случай infinite-l oop полностью корректен, хотя и немного меньше эффективнее, чем могло бы быть. И случай с бесконечным числом l oop также совершенно корректен, хотя и гораздо менее эффективен, чем мог бы быть.