Что может сделать JIT-компилятор, чего не может компилятор AOT?
Теоретически;ничего, потому что AOT-компилятор может вставить JIT-компилятор в результирующий код, если он хочет (и / или может сгенерировать самоизменяющийся код, сгенерировать 123 альтернативные версии и выбрать, какую версию использовать на основе информации времени выполнения, ...).
На практике;AOT-компилятор ограничен тем, насколько сложен разработчик компилятора, с каким языком он компилируется и как используется компилятор.Например, некоторые компиляторы (ICC от Intel) генерируют несколько версий кода и (во время выполнения) решают, какую версию использовать, исходя из того, на каком процессоре он работает, но большинство компиляторов не предназначены для этого;многие языки не предоставляют никакого способа управления «локальностью» (и уменьшают вероятность пропусков TLB и кеша);и часто компилятор используется таким образом, что создает барьеры, препятствующие оптимизации (например, отдельные «единицы компиляции» / объектные файлы, которые связаны позже, возможно, включая динамическое связывание, когда компилятор AOT не может выполнить полную оптимизацию программы и толькоможно оптимизировать детали в изоляции).Все эти вещи являются деталями реализации, а не ограничением AOT.
Другими словами;на практике «AOT vs. JIT» - это сравнение реализаций, а не настоящее сравнение самого «AOT vs. JIT»;и на практике AOT дает низкую производительность из-за подробностей реализации, а JIT дает явно хуже, чем плохую производительность, потому что сам JIT плох (дорогие оптимизации вообще не жизнеспособны, потому что они выполняются во время выполнения);и единственная причина, по которой JIT кажется «почти таким же хорошим», заключается в том, что он «почти так же хорош, как и плохой».