Користувач:The Lord Weider/Чернетка

Матеріал з Вікіпедії — вільної енциклопедії.
Перейти до навігації Перейти до пошуку

Переклад статті про JIT-компіляцію[ред. | ред. код]

Загальний огляд[ред. | ред. код]

В системах з компіляцією у байткод, вхідний код транслюється в проміжну репрезентацію, яка називається байткодом. Отриманий байткод не є машинним кодом для якоїсь конкретної архітектури процесора та може бути портований на системи будь-якої архітектури машинного кода. В подальшому, байткод може бути інтерпретованим або виконаним віртуальною машиною. JIT-компілятор читає необхідні секції (в деяких випадках - весь) байткод та динамічно компілює в машинний код. Процес генерації машинного коду може відбуватися частинами: окремими файлами, окремими функціями або окремими ділянками коду. Код може бути отриманий безпосередньо перед виконанням (звідси і термін "точно вчасно" англ. "just-in-time"), та може бути кешований для подальшого використання без повторної компіляції.

In a bytecode-compiled system, source code is translated to an intermediate representation known as bytecode. Bytecode is not the machine code for any particular computer, and may be portable among computer architectures. The bytecode may then be interpreted by, or run on a virtual machine. The JIT compiler reads the bytecodes in many sections (or in full, rarely) and compiles them dynamically into machine code so the program can run faster. This can be done per-file, per-function or even on any arbitrary code fragment; the code can be compiled when it is about to be executed (hence the name "just-in-time"), and then cached and reused later without needing to be recompiled.

In contrast, a traditional interpreted virtual machine will simply interpret the bytecode, generally with much lower performance. Some interpreters even interpret source code, without the step of first compiling to bytecode, with even worse performance. Statically-compiled code or native code is compiled prior to deployment. A dynamic compilation environment is one in which the compiler can be used during execution. A common goal of using JIT techniques is to reach or surpass the performance of static compilation, while maintaining the advantages of bytecode interpretation: Much of the "heavy lifting" of parsing the original source code and performing basic optimization is often handled at compile time, prior to deployment: compilation from bytecode to machine code is much faster than compiling from source. The deployed bytecode is portable, unlike native code. Since the runtime has control over the compilation, like interpreted bytecode, it can run in a secure sandbox. Compilers from bytecode to machine code are easier to write, because the portable bytecode compiler has already done much of the work.

JIT code generally offers far better performance than interpreters. In addition, it can in some cases offer better performance than static compilation, as many optimizations are only feasible at run-time:[1][2]

  1. The compilation can be optimized to the targeted CPU and the operating system model where the application runs. For example, JIT can choose SSE2 vector CPU instructions when it detects that the CPU supports them. To obtain this level of optimization specificity with a static compiler, one must either compile a binary for each intended platform/architecture, or else include multiple versions of portions of the code within a single binary.
  2. The system is able to collect statistics about how the program is actually running in the environment it is in, and it can rearrange and recompile for optimum performance. However, some static compilers can also take profile information as input.
  3. The system can do global code optimizations (e.g. inlining of library functions) without losing the advantages of dynamic linking and without the overheads inherent to static compilers and linkers. Specifically, when doing global inline substitutions, a static compilation process may need run-time checks and ensure that a virtual call would occur if the actual class of the object overrides the inlined method, and boundary condition checks on array accesses may need to be processed within loops. With just-in-time compilation in many cases this processing can be moved out of loops, often giving large increases of speed.
  4. Although this is possible with statically compiled garbage collected languages, a bytecode system can more easily rearrange executed code for better cache utilization.

Because a JIT must render and execute a native binary image at runtime, true machine-code JITs necessitate platforms that allow for data to be executed at runtime, making using such JITs on a Harvard architecture-based machine impossible - the same can be said for certain operating systems and virtual machines as well. However, a special type of "JIT" may potentially not target the physical machine's CPU architecture, but rather an optimized VM bytecode where limitations on raw machine code prevail, especially where that bytecode's VM eventually leverages a JIT to native code.[3]

Затримка під час запуску та оптимізації[ред. | ред. код]

JIT causes a slight to noticeable delay in initial execution of an application, due to the time taken to load and compile the bytecode. Sometimes this delay is called "startup time delay" or "warm-up time". In general, the more optimization JIT performs, the better the code it will generate, but the initial delay will also increase. A JIT compiler therefore has to make a trade-off between the compilation time and the quality of the code it hopes to generate. Startup time can include increased IO-bound operations in addition to JIT compilation: for example, the rt.jar class data file for the Java Virtual Machine (JVM) is 40 MB and the JVM must seek a lot of data in this contextually huge file.[4]

One possible optimization, used by Sun's HotSpot Java Virtual Machine, is to combine interpretation and JIT compilation. The application code is initially interpreted, but the JVM monitors which sequences of bytecode are frequently executed and translates them to machine code for direct execution on the hardware. For bytecode which is executed only a few times, this saves the compilation time and reduces the initial latency; for frequently executed bytecode, JIT compilation is used to run at high speed, after an initial phase of slow interpretation. Additionally, since a program spends most time executing a minority of its code, the reduced compilation time is significant. Finally, during the initial code interpretation, execution statistics can be collected before compilation, which helps to perform better optimization.[5]

The correct tradeoff can vary due to circumstances. For example, Sun's Java Virtual Machine has two major modes—client and server. In client mode, minimal compilation and optimization is performed, to reduce startup time. In server mode, extensive compilation and optimization is performed, to maximize performance once the application is running by sacrificing startup time. Other Java just-in-time compilers have used a runtime measurement of the number of times a method has executed combined with the bytecode size of a method as a heuristic to decide when to compile.[6] Still another uses the number of times executed combined with the detection of loops.[7] In general, it is much harder to accurately predict which methods to optimize in short-running applications than in long-running ones.[8]

Native Image Generator (Ngen) by Microsoft is another approach at reducing the initial delay.[9] Ngen pre-compiles (or "pre-JITs") bytecode in a Common Intermediate Language image into machine native code. As a result, no runtime compilation is needed. .NET framework 2.0 shipped with Visual Studio 2005 runs Ngen on all of the Microsoft library DLLs right after the installation. Pre-jitting provides a way to improve the startup time. However, the quality of code it generates might not be as good as the one that is JITed, for the same reasons why code compiled statically, without profile-guided optimization, cannot be as good as JIT compiled code in the extreme case: the lack of profiling data to drive, for instance, inline caching.[10]

There also exist Java implementations that combine an AOT (ahead-of-time) compiler with either a JIT compiler (Excelsior JET) or interpreter (GNU Compiler for Java).

Історія розвитку JIT-компіляції, її основних підходів[ред. | ред. код]

Найпершим опублікованим JIT-компілятором вважається робота Джона Маккарті над LISP у 1960.[11] В його статті "Recursive functions of symbolic expressions and their computation by machine, Part I" (англ. Рекурсивні функції символічних виразів та їхнє обчислення машинами, Частина 1), він згадує функції, що транслюються під час роботи програми, уникаючи необхідності збереження вихідного коду компілятора на перфокартах.[11] (кращим терміном для описаної системи буде "Система компіляції та запуску" (англ. compile and go system). Іншим раннім застосуванням JIT-компіляції є робота Кена Томпсона, шаблонований пошук текстового редактора QED, в якому використовувалась JIT-компіляція регулярних виразів у машинний код IBM 7094, під керівництвом ОС Compatible Time-Sharing System.[11] Великий вплив мав спосіб отримання машинного коду через інтерпретацію, який був використаний у імплементації експериментальної мови програмування LC² компанією Mitchell у 1970 році.[12][13]

Мова Smalltalk містила в собі новаторські аспекти JIT-компіляції. Наприклад, трансляція машинного коду виконувалась за потребою, а результат компіляції кешувався для подальшого використання. У випадку нестачі пам'яті, система видаляла видаляла частинки цього коду та відновлювала новою компіляцією за потреби.[14][15] Мова Self, "діалект" мови Smalltalk що був розроблений компанією Sun, розвинула ці техніки та певний час була найшвидшою з сімейства Smalltalk, досягаючи половини швидкості оптимізованого коду на C[16], будучи повністю об'єктно-орієнтованою мовою.

Згодом Sun припинили активну розробку Self, однак використали отриманий досвід у мові Java. Термін "Just-in-time компіляція" був запозичений з виробничого терміну "Just in time" та набув популярності у Java - Джеймс Гослінг використовував цей термін з 1993.[17] Зараз JIT-компіляція використовується більшістю імплементацій віртуальної машини Java, оскільки HotSpot бере за основу та активно використовує цю технологію.

Проєкт Dynamo[18] компанії HP був експериментальним JIT-компілятором, в якому формат байткоду відповідав машинному коду, система переводила машинний код PA-6000 у машинний код PA-8000. Це призвело до збільшення швидкодії, у деяких випадках до 30%, оскільки відкрило можливість використовувати оптимізації на рівні машинного коду, наприклад, вбудовування коду для кращого використання кеш-пам'яті, оптимізації викликів динамічних бібліотек та інші, що доступні тільки під час безпосереднього виконання, що робить їх проблематичними для використання звичайними компіляторами.[19][20]

30 березня 2019 року було анонсовано, що PHP 8 отримає JIT-компіляцію[21] у 2021 році.[22]

Питання безпеки[ред. | ред. код]

JIT-компіляція вимагає більшої уваги до питань безпеки та несе підвищені ризики, оскільки має на меті виконання автогенерованого машинного коду. Скомпільований код зберігається в пам'ять та одразу виконується.

Цей процес відрізняється від виконання заздалегідь скомпільованого машинного коду тим, що у випадку JIT-компіляції процесор має виконувати код з загальної ділянки пам'яті. Це суперечить ідеї захисту виконавчої ділянки пам'яті, за якої виконання машинного коду має бути дозволене тільки з спеціально відмічених ділянок пам'яті, та навпаки - виконання коду з загальної пам'яті заборонене, оскільки це є слабким місцем захисту від зовнішніх втручань. З цієї причини, сегменти пам'яті з кодом, який був скомпільований на льоту, мають бути відмічені як виконавчі сегменти. З міркувань безпеки, виконавча помітка має бути виставлена після 1. запису коду в пам'ять та 2. виставлення помітки тільки для читання (read-only), оскільки одночасний дозвіл на запис та виконання сегменту пам'яті є потенційною небезпекою (див. W^X).[23] Для прикладу, Javascript JIT-компілятор Firefox'а отримав таку імплементацію у версії Firefox 46. [24]

JIT spraying є підвидом експлойта, що використовує JIT-компіляцію як елемент heap spraying атаки, що дозволяє обійти ASLR та захист виконавчого простору, заповнивши купу виконавчим кодом. [25]

Див. також[ред. | ред. код]

Оскільки для

Notes[ред. | ред. код]

References[ред. | ред. код]

Citations[ред. | ред. код]

  1. Croce, Louis. Just in Time Compilation (PDF). Columbia University. Архів оригіналу (PDF) за 3 травня 2018.
  2. What are the advantages of JIT vs. AOT compilation. Stack Overflow. Jan 21, 2010.
  3. javascript - Compile a JIT based lang to Webassembly. Stack Overflow. Процитовано 4 грудня 2018.
  4. Haase, Chet (May 2007). Consumer JRE: Leaner, Meaner Java Technology. Sun Microsystems. Процитовано 27 липня 2007. At the OS level, all of these megabytes have to be read from disk, which is a very slow operation. Actually, it's the seek time of the disk that's the killer; reading large files sequentially is relatively fast, but seeking the bits that we actually need is not. So even though we only need a small fraction of the data in these large files for any particular application, the fact that we're seeking all over within the files means that there is plenty of disk activity.
  5. The Java HotSpot Performance Engine Architecture. Oracle.com. Процитовано 5 липня 2013.
  6. Schilling, Jonathan L. (February 2003). The simplest heuristics may be the best in Java JIT compilers (PDF). SIGPLAN Notices. 38 (2): 36—46. doi:10.1145/772970.772975. Архів оригіналу (PDF) за 24 вересня 2015.
  7. Toshio Suganuma, Toshiaki Yasue, Motohiro Kawahito, Hideaki Komatsu, Toshio Nakatani, "A dynamic optimization framework for a Java just-in-time compiler", Proceedings of the 16th ACM SIGPLAN conference on Object-oriented programming, systems, languages, and applications (OOPSLA '01), pp. 180–195, October 14–18, 2001.
  8. Matthew Arnold, Michael Hind, Barbara G. Ryder, "An Empirical Study of Selective Optimization", Proceedings of the 13th International Workshop on Languages and Compilers for Parallel Computing-Revised Papers, pp. 49–67, August 10–12, 2000.
  9. Native Image Generator (Ngen.exe). Msdn2.microsoft.com. Процитовано 5 липня 2013.
  10. Matthew R. Arnold, Stephen Fink, David P. Grove, Michael Hind, and Peter F. Sweeney, "A Survey of Adaptive Optimization in Virtual Machines", Proceedings of the IEEE, 92(2), February 2005, pp. 449–466.
  11. а б в Aycock, 2003, 2. JIT Compilation Techniques, 2.1 Genesis, p. 98.
  12. Aycock, 2003, 2. JIT Compilation Techniques, 2.2 LC², p. 98–99.
  13. Mitchell, J.G. (1970). The design and construction of flexible and efficient interactive programming systems.
  14. Aycock, 2003.
  15. Deutsch, L.P.; Schiffman, A.M. (1984). Efficient implementation of the Smalltalk-80 system (PDF). POPL '84: Proceedings of the 11th ACM SIGACT-SIGPLAN Symposium on Principles of Programming Languages: 297—302. doi:10.1145/800017.800542. ISBN 0-89791-125-3. Архів оригіналу (PDF) за 18 червня 2004.
  16. [1] [Архівовано November 24, 2006, у Wayback Machine.]
  17. Aycock, 2003, 2.14 Java, p. 107, footnote 13.
  18. "Dynamo: A Transparent Dynamic Optimization System" Vasanth Bala, Evelyn Duesterwald, Sanjeev Banerjia - PLDI '00 Proceedings of the ACM SIGPLAN 2000 conference on Programming language design and implementation - pages 1 to 12 - DOI:10.1145/349299.349303. Retrieved March 28, 2012
  19. John Jannotti. HP's Dynamo - Page 1 - (3/2000). Ars Technica. Процитовано 5 липня 2013.
  20. The HP Dynamo Project. Архів оригіналу за 19 жовтня 2002. Процитовано 12 квітня 2016.{{cite web}}: Обслуговування CS1:Сторінки з посиланнями на джерела, що мають непридатні URL (посилання)
  21. PHP Gr8.
  22. PHP 8 and 7.4 to come with Just-in-time (JIT) to make most CPU-intensive workloads run significantly faster. April 2019.
  23. "How to JIT – an introduction", Eli Bendersky, November 5th, 2013 at 5:59 am
  24. De Mooij, Jan. W^X JIT-code enabled in Firefox. Jan De Mooij. Процитовано 11 May 2016.
  25. Jürgen Schmidt (20 January 2011). Return of the sprayer -- JIT Spraying: Exploits to beat DEP and ASLR. The H. Процитовано 22 January 2011.

Джерела[ред. | ред. код]

External links[ред. | ред. код]