You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
In bensku's Long-term plans on Skript he mentions Skript-to-JVM compilation as a future possibility.
I'd like to detail a few different options and approaches to this, so that we can discuss which would be most suitable for Skript to use if (or when) it becomes a reality.
Introduction: what are the benefits of compiling?
A language compiled to bytecode can make better use of Hotspot's JIT than an interpreted language.
While iterating over a list of consumer instructions (as a basic example) can be 'warmed up' to some extent, the JIT compiler will have a much easier time improving real code. Java's JIT compilers are incredibly smart and are capable of spotting logical inefficiencies when compiling the tree representation to native code. This means that JIT can, to an extent, 'improve' poor code.
It is much more difficult for an interpreted language to take advantage of this as the actual functional "code" is locked behind another layer of abstraction making it much more difficult to spot common inefficiencies (see: useless loops, small functions that should be in-lined, repeat get calls that could be saved as a constant value.)
Now this does not mean the JIT compilers are a magical 'fix-all' solution, but they do have parts we can take advantage of.
A language compiled to bytecode has the potential to be more efficient than an interpreted language.
This, again, boils down to layers of abstraction. An interpreted language will always be wrapped in method/function calls and iterative processes that can be avoided entirely when compiling (since the instructions are all written in-situ.)
Once again, the efficiency gained does depend on how well the compiler is made.
A language compiled to bytecode can be more memory-efficient.
By taking advantage of the stack (and simply needing fewer objects to be created during processing) we can not only use less memory but also rely more on Java's built-in garbage collection and finalisation, rather than having to worry about things like disposing of local variables (and knowing when it is safe to do so.) On top of this, the more the language relies on the JVM's built-in tools the more reliable it is likely to be since the JVM is very widely-used and well-tested.
Compiling removes the need for repeat interpretation.
If the compilation result is stored, scripts would not need re-interpreting on every load as long as there is some way to reliably determine whether they had been changed. This would reduce (re)load time significantly if used properly.
Suitable options for Skript
There are multiple ways we can use compilation to improve Skript, and each way has specific advantages and disadvantages. I will try and detail these below.
Simple class compilation
This would involve generating a single .class for each script file, with some sort of annotation data to designate entry-points (like commands, functions, events, etc.) that Skript could use when loading them.
Simple syntax instructions could be converted into raw bytecode, and complex (or addon) syntax could have the execute/get/check methods called directly to preserve compatibility, similar to the idea behind BigBadE's project.
Bytecode is, paradoxically, a much more forgiving language to write than Java since it is very lenient and a lot of minor errors can be ignored. This means it wouldn't be hugely difficult to convert Skript code into bytecode instructions.
Note: the class result could be run through JAOTC to create a native image.
Advantages
Disadvantages
Fairly compatible with existing addons and API. The Skript code would simply invoke the syntax itself rather than needing the parser to do it.
Result class would require reflection to find and run triggers, commands, events, etc.
Comparison would be simple: one script = one class, each root-level entry in the script would have a static method generated for the trigger with metadata kept in annotations.
Would not make the most of bytecode efficiency since local variables, etc. would still have their existing problems.
The result code would be filled with needless method calls that should ideally be in-lined.
Full/deep class compilation
This would involve trying to generate all of the code elements as though they had come from a real plugin, such as proper Bukkit-compatible command classes and event listeners, generating real bytecode for maths expressions and other simple calls, generating real scheduler calls for timings, waits, etc.
This could end up with multiple files (or at least nested classes) for many elements, and some more advanced addons might end up with syntax incompatibilities if some syntax calls simply couldn't be made to fit the bytecode format.
Syntax classes could provide the inline instructions for generating the bytecode, instead of a method containing the code.
Advantages
Disadvantages
Very efficient, much of the result would be comparable to the speed of a real Java plugin.
A lot more complicated to design and develop.
Reduced need for reflection when using entry points, etc. since much of it would be Bukkit-compatible.
Almost certainly incompatible with a lot of current elements, which would need some major re-designs.
Future-proof.
Some Skript elements like timings might not be easily convertible to a bytecode instruction and could end up having side-effects.
Just-in-time 'targeted' compilation
Instead of compiling entire scripts to bytecode, this treatment could instead be reserved for key parts. Slow or natively inefficient Skript syntaxes could be targets for compilation or areas where there would be a real benefit (see loops, list-access, etc.) similar to how Java designates some methods as targets for JIT compilation where large speed improvements can be made.
This would generate class stubs at runtime that would need to be regenerated on every load, similar to the JVM's JIT system.
Advantages
Disadvantages
Unlikely to cause any compatibility issues.
The result would not be re-usable so requires re-compilation on load.
Improvements could be targeted to problem areas rather than requiring the entire script to be compiled.
Only targeted parts would benefit from efficiency improvements.
Has the benefit of runtime insights: can actively time executions to find slow areas that might benefit.
The scope of any improvement would be restricted by how much was compiled.
Conclusion
Skript has multiple options when it comes to compiling for the JVM. Some of these would almost certainly require major rewrites of the API (and potentially addons) in order to get the benefit of them, whereas others (like JIT-style targeted compilation) would be easier to integrate into the existing plugin at the cost of some of the bonus efficiency and improvements.
I'd like to invite any discussion about which style people think is most appropriate for Skript (perhaps you think a big rewrite to get the benefit of major improvements is necessary, or perhaps you think Skript should aim to preserve backwards compatibility) and also about what areas of Skript people think would most benefit from compilation so we can work out how to get the best improvement.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
-
In bensku's Long-term plans on Skript he mentions Skript-to-JVM compilation as a future possibility.
I'd like to detail a few different options and approaches to this, so that we can discuss which would be most suitable for Skript to use if (or when) it becomes a reality.
Introduction: what are the benefits of compiling?
While iterating over a list of consumer instructions (as a basic example) can be 'warmed up' to some extent, the JIT compiler will have a much easier time improving real code. Java's JIT compilers are incredibly smart and are capable of spotting logical inefficiencies when compiling the tree representation to native code. This means that JIT can, to an extent, 'improve' poor code.
It is much more difficult for an interpreted language to take advantage of this as the actual functional "code" is locked behind another layer of abstraction making it much more difficult to spot common inefficiencies (see: useless loops, small functions that should be in-lined, repeat get calls that could be saved as a constant value.)
Now this does not mean the JIT compilers are a magical 'fix-all' solution, but they do have parts we can take advantage of.
This, again, boils down to layers of abstraction. An interpreted language will always be wrapped in method/function calls and iterative processes that can be avoided entirely when compiling (since the instructions are all written in-situ.)
Once again, the efficiency gained does depend on how well the compiler is made.
By taking advantage of the stack (and simply needing fewer objects to be created during processing) we can not only use less memory but also rely more on Java's built-in garbage collection and finalisation, rather than having to worry about things like disposing of local variables (and knowing when it is safe to do so.) On top of this, the more the language relies on the JVM's built-in tools the more reliable it is likely to be since the JVM is very widely-used and well-tested.
If the compilation result is stored, scripts would not need re-interpreting on every load as long as there is some way to reliably determine whether they had been changed. This would reduce (re)load time significantly if used properly.
Suitable options for Skript
There are multiple ways we can use compilation to improve Skript, and each way has specific advantages and disadvantages. I will try and detail these below.
Simple class compilation
This would involve generating a single
.class
for each script file, with some sort of annotation data to designate entry-points (like commands, functions, events, etc.) that Skript could use when loading them.Simple syntax instructions could be converted into raw bytecode, and complex (or addon) syntax could have the
execute/get/check
methods called directly to preserve compatibility, similar to the idea behind BigBadE's project.Bytecode is, paradoxically, a much more forgiving language to write than Java since it is very lenient and a lot of minor errors can be ignored. This means it wouldn't be hugely difficult to convert Skript code into bytecode instructions.
Note: the class result could be run through JAOTC to create a native image.
Full/deep class compilation
This would involve trying to generate all of the code elements as though they had come from a real plugin, such as proper Bukkit-compatible command classes and event listeners, generating real bytecode for maths expressions and other simple calls, generating real scheduler calls for timings, waits, etc.
This could end up with multiple files (or at least nested classes) for many elements, and some more advanced addons might end up with syntax incompatibilities if some syntax calls simply couldn't be made to fit the bytecode format.
Syntax classes could provide the inline instructions for generating the bytecode, instead of a method containing the code.
Just-in-time 'targeted' compilation
Instead of compiling entire scripts to bytecode, this treatment could instead be reserved for key parts. Slow or natively inefficient Skript syntaxes could be targets for compilation or areas where there would be a real benefit (see loops, list-access, etc.) similar to how Java designates some methods as targets for JIT compilation where large speed improvements can be made.
This would generate class stubs at runtime that would need to be regenerated on every load, similar to the JVM's JIT system.
Conclusion
Skript has multiple options when it comes to compiling for the JVM. Some of these would almost certainly require major rewrites of the API (and potentially addons) in order to get the benefit of them, whereas others (like JIT-style targeted compilation) would be easier to integrate into the existing plugin at the cost of some of the bonus efficiency and improvements.
I'd like to invite any discussion about which style people think is most appropriate for Skript (perhaps you think a big rewrite to get the benefit of major improvements is necessary, or perhaps you think Skript should aim to preserve backwards compatibility) and also about what areas of Skript people think would most benefit from compilation so we can work out how to get the best improvement.
Beta Was this translation helpful? Give feedback.
All reactions