Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More scripting languages #293

Open
jbaiter opened this issue Jun 23, 2019 · 8 comments
Open

More scripting languages #293

jbaiter opened this issue Jun 23, 2019 · 8 comments

Comments

@jbaiter
Copy link

jbaiter commented Jun 23, 2019

Seeing that Cantaloupe's Ruby scripting support is based on the JSR223 APIs, would there be interest in adding support for more scripting languages?
I have a working prototype with JavaScript (Graal.js or Nashorn) and Python 2.x (Jython) support that I could fashion into a PR if this is something that is deemed useful for the project.

@adolski
Copy link
Contributor

adolski commented Jun 24, 2019

Could you tell me more about JS via GraalVM? I half-watched one of Oracle's presentations about GraalVM and it made me think it would be great if there were a way to run Cantaloupe in GraalVM and leave the language support up to Graal, i.e. remove JRuby. But I'm not yet familiar with Graal's APIs or how it works/doesn't work with JSR-223 etc.

Setting Graal aside, I'm open to making more languages available via JSR-223, although maybe not bundling them in.

@jbaiter
Copy link
Author

jbaiter commented Jun 24, 2019

I'm not that knowledgeable about GraalVM myself, but I think the gist is that it's in part a generic language runtime built on the JVM, with implementations for a number of languages like JavaScript, Ruby, Python or even LLVM IR code (so for basically any language supported by LLVM). There's also another part that can compile programs to native code, but that's not relevant for our scripting use case, I think.
And Graal.js and TruffleRuby (its Ruby implementation) optionally expose their language runtime via the JSR-223 APIs, i.e. just like JRuby.

You can check out my branch with Graal.js support here: https://github.com/jbaiter/cantaloupe/tree/more-scripting-langs/src/main/java/edu/illinois/library/cantaloupe/script/engines It's basically just another JSR-223 engine that gets loaded, just like JRuby.

Bundling those language runtimes in is certainly not ideal. Graal.js adds about 23MiB to the artifact size and Jython ~40MiB. JavaScript via Nashorn would have no impact on the size, since it comes with the JVM itself, but it's marked as deprecated since Java 11, so it's not a long-term solution. An option would be to bake support for a number of engines into Cantaloupe (i.e. method name mappings, proxy object construction, etc), but not ship the actual engines in the final WAR. Users would then have to download the JAR for the language of their choice and instruct the JVM to load the language runtime into the Cantaloupe classpath.

@adolski
Copy link
Contributor

adolski commented Jun 26, 2019

Agreed about the bundling.

That is great work, by the way!

I've looked into this some more and found the GraalVM Polyglot API. This looks like an alternative to JSR-223 that would support any Truffle language when Cantaloupe itself is running in GraalVM. And GraalVM provides a tool for installing languages so that would no longer have to be Cantaloupe's responsibility.

There are several reasons I'm interested in this route instead of JSR-223:

  1. Being able to unbundle JRuby from the application and use more languages (gu install <language> being a little more convenient than downloading JARs as you mentioned)
  2. The Truffle-based implementations are all newer & faster than e.g. Jython, JRuby, Nashorn
  3. GraalVM should provide better performance for Cantaloupe itself
  4. GraalVM allows access to native libraries more conveniently than JNI (this doesn't have anything to do with the delegate script, but the inconvenience of accessing native libraries from Java is holding back other aspects of the project)
    a. Upon closer inspection, this feature may only be available in the "GraalVM Enterprise Edition" ($$$$)
  5. It seems like Oracle is putting a lot of resources into Graal, and not as much is happening in the JSR-223 area

Apparently GraalVM doesn't yet fully support Java 11: oracle/graal#651

And I don't know if there are other features in Cantaloupe that wouldn't work in Graal. Might have to test it out and see.

So, it's kind of a complicated situation, but I would like to explore GraalVM further before investing more effort into JSR-223.

@jbaiter
Copy link
Author

jbaiter commented Jul 3, 2019

GraalVM allows access to native libraries more conveniently than JNI (this doesn't have anything to do with the delegate script, but the inconvenience of accessing native libraries from Java is holding back other aspects of the project)

This is unrelated to the actual scripting issue here, but a far better/more ergonomic alternative to JNI is JNR-FFI. I wrote two ImageIO adapters for TurboJPEG and OpenJPEG with it, and it was a joy to work with: https://github.com/dbmdz/imageio-jnr

It works across many JVM versions and architectures, and a huge advantage is that users usually don't have to compile anything, since you can directly talk to the shared libraries already on the system over the ABI.


edit: So I just ran the no-deps test suite with the latest GraalVM release (19.1.0) and almost all tests are passing! The only failing tests are the ones for S3, because of an inability to locate the ./test.properties file, but that should be an easy fix.
The native dependencies will likely need more work, since they need to be re-compiled with GraalVM, but maybe this could be a chance to evaluate if JNR-FFI is not better suited for accessing them.

I also ran the benchmarks with both GraalVM and OpenJDK 11. Generally, GraalVM is a bit slower than the JVM, but always within the measurement error. Also, the benchmarks were run in interpreteted mode, not with AOT compilation, which might give more of a performance boost.

@adolski
Copy link
Contributor

adolski commented Jul 5, 2019

This is unrelated to the actual scripting issue here, but a far better/more ergonomic alternative to JNI is JNR-FFI. I wrote two ImageIO adapters for TurboJPEG and OpenJPEG with it, and it was a joy to work with: https://github.com/dbmdz/imageio-jnr

It works across many JVM versions and architectures, and a huge advantage is that users usually don't have to compile anything, since you can directly talk to the shared libraries already on the system over the ABI.

This is great! I had heard of JNR, but didn't really know anything about it. I will definitely take a closer look when I have time.


edit: So I just ran the no-deps test suite with the latest GraalVM release (19.1.0) and almost all tests are passing! The only failing tests are the ones for S3, because of an inability to locate the ./test.properties file, but that should be an easy fix.
The native dependencies will likely need more work, since they need to be re-compiled with GraalVM, but maybe this could be a chance to evaluate if JNR-FFI is not better suited for accessing them.

I also ran the benchmarks with both GraalVM and OpenJDK 11. Generally, GraalVM is a bit slower than the JVM, but always within the measurement error. Also, the benchmarks were run in interpreteted mode, not with AOT compilation, which might give more of a performance boost.

Interesting. Well, it looks promising. The S3 stuff doesn't worry me (I'm sure it would pass). The JNI users in the freedeps suite are the big question marks...

@adolski
Copy link
Contributor

adolski commented Jan 8, 2020

I've had time recently to look into GraalVM and its Polyglot API. I was successful in swapping in TruffleRuby+Polyglot in place of JRuby+JSR-223. The existing delegate script is mostly compatible except the syntax of invoking Java code from within the script is a little different. And gems work.

JRuby is still bundled in for compatibility with regular JVMs, but if it were to be removed, it would reduce the size of the WAR by about 30%.

Next I moved on to GraalJS, with a new JavaScript version of the delegate script. This also works.

I didn't try GraalPython yet--that one advertises itself as less mature, so I don't know.

Work is in progress on the feature/graalvm branch on that branch. The current state of that branch is that JRuby is used as usual in an ordinary JVM, but TruffleRuby & GraalJS are used in GraalVM. (The language is inferred from the delegate script filename.)

@cmhdave
Copy link
Contributor

cmhdave commented Jul 2, 2020

Somewhat related, we've been doing some heavy load performance testing with our installed instance and we're seeing significant blocking threads with JRuby. This is a typical blocked thread:

edu.illinois.library.cantaloupe.script.JRubyDelegateProxy.instantiateDelegate(JRubyDelegateProxy.java:119)
edu.illinois.library.cantaloupe.script.JRubyDelegateProxy.setRequestContext(JRubyDelegateProxy.java:138)
edu.illinois.library.cantaloupe.script.JRubyDelegateProxy.invokeUncached(JRubyDelegateProxy.java:357)
org.jruby.embed.jsr223.JRubyEngine.invokeMethod(JRubyEngine.java:215)
org.jruby.embed.ScriptingContainer.callMethod(ScriptingContainer.java:1463)
org.jruby.embed.internal.EmbedRubyObjectAdapterImpl.callMethod(EmbedRubyObjectAdapterImpl.java:170)
org.jruby.embed.internal.EmbedRubyObjectAdapterImpl.call(EmbedRubyObjectAdapterImpl.java:304)
org.jruby.embed.internal.BiVariableMap.inject(BiVariableMap.java:384)
org.jruby.embed.variable.VariableInterceptor.inject(VariableInterceptor.java:142)
org.jruby.embed.variable.Constant.inject(Constant.java:212)
org.jruby.runtime.opto.ConstantInvalidator.invalidate(ConstantInvalidator.java:20)
org.jruby.runtime.opto.SwitchPointInvalidator.invalidate(SwitchPointInvalidator.java:41)
    BLOCKED

That SwitchPointInvalidator.invalidate method is synchronized and ends up giving us slow response. I'll be interested to see how GraalVM handles a similar heavy load.

@barryspearce
Copy link

Slightly unrelated but on the same idea, I am a java developer that knows python, but not ruby, however, I would rather be able to supply the necessary class in Java rather than yet another language. I am having to struggle with Ruby just to deal with the delegate script - which should be quick but is proving to be anything other than that... :( Especially as all time dealing with Ruby for me is wasted effort because I doubt I will be using Ruby again in the next 30-40 years by which time I probably won't care any more...

I would have liked an option to supply a jar or class file containing the necessary functions - that means I don't have to spend any time messing around struggling with new languages when I can use Java.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants