龙空技术网

GraalVM要做的十件事

闻数起舞 1403

前言:

目前咱们对“apache来宾用户”都比较注重,各位老铁们都想要分析一些“apache来宾用户”的相关资讯。那么小编也在网摘上汇集了一些对于“apache来宾用户””的相关知识,希望朋友们能喜欢,小伙伴们一起来学习一下吧!

此帖已更新到GraalVM 19.3.0

GraalVM有很多不同的部分,因此,如果您以前听过这个名字,或者甚至看过我们的一些谈话,那么肯定可以做的事情您还不知道。 在本文中,我们将列出GraalVM的一些不同功能,并向您展示它们可以为您做些什么。

· 1.高性能现代Java

· 2.占地面积小,启动迅速的Java

· 3.结合使用JavaScript,Java,Ruby和R

· 4.在JVM上运行本地语言

· 5.适用于所有语言的工具

· 6.扩展基于JVM的应用程序

· 7.扩展本机应用程序

· 8.Java代码作为本机库

· 9.数据库中的多语种

· 10.创建自己的语言

您可以使用graalvm.org/downloads复制GraalVM 19.3.0中我在本文中显示的所有内容。 我在macOS上使用企业版,可在此处进行免费评估,但说明也可在Linux上使用。 他们中的大多数人还将与社区版一起使用。

在阅读的同时,继续并运行这些程序! 我在GraalVM上运行的代码可以从github.com/chrisseaton/graalvm-ten-things/中复制。

建立

我已经从下载了基于macOS的基于JDK8的GraalVM Enterprise Edition,并将其中的程序放入了我的$ PATH中。 默认情况下,这为我提供了Java和JavaScript语言。

$ git clone  cd foo$ tar -zxf graalvm-ee-java8-darwin-amd64-19.3.0.tar.gz # or graalvm-ee-java8-linux-amd64-19.3.0.tar.gz on Linux$ export PATH=graalvm-ee-java8-19.3.0/Contents/Home/bin:$PATH # or PATH=graalvm-ee-java8-19.3.0/bin:$PATH on Linux

GraalVM随附了JavaScript,并具有一个名为gu的软件包管理器,可让您安装其他语言。 我已经安装了Ruby,Python和R语言。 我还安装了native-imagetool。 这些都可以从GitHub下载。

$ gu install native-image$ gu install ruby$ gu install python$ gu install R

现在,当您运行Java或js时,将获得这些运行时的GraalVM版本。

$ java -versionjava version "1.8.0_231"Java(TM) SE Runtime Environment (build 1.8.0_231-b11)Java HotSpot(TM) 64-Bit GraalVM EE 19.3.0 (build 25.231-b11-jvmci-19.3-b05, mixed mode)$ js --versionGraalVM JavaScript (GraalVM EE Native 19.3.0)
1.高性能现代Java

GraalVM中的Graal名称来自GraalVM编译器。 GraalVM是一个可以全部处理的编译器,这意味着它是作为库编写的编译器的单个实现,可以用于许多不同的事情。 例如,我们使用GraalVM编译器提前编译和及时编译,编译多种编程语言,并编译成多种架构。

使用GraalVM的一种简单方法是将其用作Java JIT编译器。

我们将使用此示例程序,该程序为您提供文档中的前十个单词。 它使用了现代Java语言功能,例如流和收集器。

GraalVM包含一个javac编译器,但与本演示相比,它与标准编译器没有什么不同,因此,如果您愿意,可以改用系统javac。

$ javac TopTen.java

如果我们运行GraalVM中包含的java命令,我们将自动使用Graal JIT编译器-不需要额外的配置。 我将使用time命令来获取从头到尾运行整个程序所花费的真实时间,而不是设置复杂的微基准,而我将使用较大的输入,以便我们不会在这里或那里徘徊几秒钟。 large.txt文件为150 MB。

$ make large.txt$ time java TopTen large.txtsed = 502701ut = 392657in = 377651et = 352641id = 317627eu = 317627eget = 302621vel = 300120a = 287615sit = 282613real 0m12.950suser 0m17.827ssys 0m0.622s

GraalVM用Java编写,而不是像大多数其他Java的JIT编译器那样用C ++编写。 我们认为,这可以使我们比现有的编译器更快地进行改进,并进行了强大的新优化,例如部分转义分析,而HotSpot的标准JIT编译器中则没有。 这可以使您的Java程序运行明显更快。

要在没有GraalVM JIT编译器进行比较的情况下运行,我可以使用-XX:-UseJVMCICompiler标志。 JVMCI是GraalVM和JVM之间的接口。 您也可以将其与标准JVM进行比较。

$ time java -XX:-UseJVMCICompiler TopTen large.txtsed = 502701ut = 392657in = 377651et = 352641id = 317627eu = 317627eget = 302621vel = 300120a = 287615sit = 282613real 0m19.602suser 0m20.357ssys 0m0.498s

这表明GraalVM在使用标准HotSpot编译器运行Java程序大约需要三分之二的时间来运行我们的Java程序。 在我们习惯将单位数百分比的性能提升视为显着的领域中,这是一个很大的改进。

如果使用社区版,您仍然可以获得比HotSpot更好的结果,但是它不如企业版那么好。

Twitter是当今在生产中使用GraalVM的一家公司,他们说,对于他们而言,它在节省的实际资金方面获得了回报。 Twitter正在使用GraalVM运行Scala应用程序— GraalVM在JVM字节码级别上工作,因此它适用于任何JVM语言。

这是使用GraalVM的第一种方法-只是将其作为现有Java应用程序的更好的嵌入式JIT编译器。

2.足迹小,启动迅速的Java

Java平台对于长时间运行的进程和最高的性能特别强大,但是短期运行的进程可能会遭受更长的启动时间和相对较高的内存使用率。

例如,如果我们使用较小的输入(大约1 KB而不是150 MB)运行相同的应用程序,那么运行这么小的文件似乎花费了不合理的长时间,并且70 MB的内存很大。 。 我们使用-l打印所用的内存以及所用的时间。

$ make small.txt$ /usr/bin/time -l java TopTen small.txt # -v on Linux instead of -lsed = 6sit = 6amet = 6mauris = 3volutpat = 3vitae = 3dolor = 3libero = 3tempor = 2suscipit = 2 0.17 real 0.28 user 0.04 sys 70737920 maximum resident set size...

GraalVM为我们提供了解决此问题的工具。 我们说过GraalVM就像一个编译器库,可以用许多不同的方式使用。 其中之一是提前编译为本地可执行映像,而不是在运行时即时编译。 这类似于gcc之类的常规编译器的工作方式。

$ native-image --no-server --no-fallback TopTen[topten:37970] classlist: 1,801.57 ms[topten:37970] (cap): 1,289.45 ms[topten:37970] setup: 3,087.67 ms[topten:37970] (typeflow): 6,704.85 ms[topten:37970] (objects): 6,448.88 ms[topten:37970] (features): 820.90 ms[  topten:37970] analysis: 14,271.88 ms[topten:37970] (clinit): 257.25 ms[topten:37970] universe: 766.11 ms[topten:37970] (parse): 1,365.29 ms[topten:37970] (inline): 3,829.55 ms[topten:37970] (compile): 34,674.51 ms[topten:37970] compile: 41,412.71 ms[topten:37970] image: 2,741.41 ms[topten:37970] write: 619.13 ms[topten:37970] [total]: 64,891.52 ms

此命令生成一个称为topten的本机可执行文件。 该可执行文件不是JVM的启动器,它没有链接到JVM,也没有以任何方式捆绑JVM。 native-image确实可以编译您的Java代码以及您使用的所有Java库,到简单的机器代码。 对于运行时组件(如垃圾收集器),我们正在运行自己的名为SubstrateVM的新VM,类似于GraalVM也是用Java编写的。

如果我们查看使用率最高的库,您会发现它们只是标准系统库。 我们也可以仅将这一个文件移到从未安装过JVM的系统上,然后在该系统上运行以确认它不使用JVM或任何其他文件。 它也非常小-该可执行文件不到8 MB。

$ otool -L topten # ldd topten on Linuxtopten: libSystem.B.dylib (current version 1252.250.1) CoreFoundation (current version 1575.12.0) /usr/lib/libz.1.dylib (compatibility version 1.0.0, current version 1.2.11)$ du -h topten 7.5M topten

如果我们运行可执行文件,我们可以看到它比在JVM上运行相同程序的速度快大约一个数量级,并且使用的内存少大约一个数量级。 它是如此之快,以至于您没有注意到在命令行上使用它所花费的时间-当您使用JVM运行短运行命令时,您不会感觉到总是会暂停。

$ /usr/bin/time -l ./topten small.txtsed = 6sit = 6amet = 6mauris = 3volutpat = 3vitae = 3dolor = 3libero = 3tempor = 2suscipit = 2 0.02 real 0.00 user 0.00 sys 3158016 maximum resident set size...

本机映像工具有一些限制,例如在编译过程中必须使用所有类,而在反射方面有一些限制。 与基本编译相比,它还有一些其他优点,因为静态初始值设定项在编译期间运行,因此您可以减少每次加载应用程序时所做的工作。

这是您可以使用GraalVM的第二种方法-一种以低占用空间和快速启动的方式分发和运行现有Java程序的方法。 它还使您摆脱配置问题,例如在运行时查找正确的jar文件,并允许您拥有较小的Docker映像。

3.结合使用JavaScript,Java,Ruby和R

除了Java之外,GraalVM还包括JavaScript,Ruby,R和Python的新实现。 这些是使用称为Truffle的新语言实现框架编写的,该框架使实现简单而高性能的语言解释器成为可能。 当您使用Truffle编写语言解释器时,Truffle会自动代表您使用GraalVM为您的语言提供JIT编译器。 因此GraalVM不仅是Java的JIT编译器和提前的本机编译器,而且还可以是JavaScript,Ruby,R和Python的JIT编译器。

GraalVM中的语言旨在替代现有语言。 例如,我们可以安装一个Node.js模块:

$ npm install color...+ color@3.1.1added 6 packages from 6 contributors and audited 7 packages in 6.931s

我们可以使用该模块编写一个小程序color.js,将RGB HTML颜色转换为HSL:

var Color = require('color');process.argv.slice(2).forEach(function (val) {   console.log(Color(val).hsl().string());});

然后,我们可以按照通常的方式运行它:

$ node color.js '#42aaf4'hsl(204.89999999999998, 89%, 60.8%)

GraalVM中的语言可以协同工作-有一个API,可让您从一种语言运行另一种语言的代码。 这使您可以编写多语种程序-用多种语言编写的程序。

您可能想这样做,是因为您想用一种语言编写大多数应用程序,但是您想使用另一种语言生态系统中的一个库。 例如,假设我们想编写将Node.js中CSS颜色名称转换为十六进制的应用程序,但是我们想使用Ruby颜色库进行转换。

var express = require('express');var app = express();color_rgb = Polyglot.eval('ruby', `  require 'color'  Color::RGB`);app.get('/css/:name', function (req, res) {  color = color_rgb.by_name(req.params.name).html()  res.send('<h1 style="color: ' + color + '" >' + color + '</h1>');});app.listen(8080, function () {  console.log('serving at ;)});

我们指定了一些Ruby代码以字符串形式运行,但是请注意,我们在其中没有做很多事情-我们只需要库,然后返回Ruby对象即可。 从Ruby使用此对象的方式通常是说Color :: RGB.by_name(name).html。 如果您查看一下JavaScript如何进一步使用color_rgb,您会发现实际上我们是从JavaScript调用这些方法的,即使它们是Ruby对象和方法,并且将它们传递给JavaScript字符串,然后将结果连接起来, 这是一个Ruby字符串,以及其他JavaScript字符串。

我们将同时安装Ruby和JavaScript依赖项。

$ gem install colorFetching: color-1.8.gem (100%)Successfully installed color-1.81 gem installed$ npm install express+ express@4.17.0added 50 packages from 37 contributors and audited 143 packages in 22.431s

然后,我们需要使用两个选项来运行node:–polyglot表示我们希望访问其他语言,以及–jvm,因为默认情况下,节点本机映像不包括JavaScript。

$ node --polyglot --jvm color-server.jsserving at 

然后,在浏览器中正常打开http:// localhost:8080 / css / aquamarine或其他颜色名称。

让我们尝试使用更多语言和模块的更大示例。

JavaScript对于任意大的整数没有很好的解决方案。 我发现了几个类似big-integer的模块,但是这些模块效率不高,因为它们将数字的组件存储为JavaScript浮点数。 Java的BigInteger类效率更高,因此让我们使用它来执行一些任意大的整数运算。

JavaScript还不包含对图形的任何内置支持,而R确实对此提供了出色的支持。 让我们使用R的svg模块绘制三角函数的3D散点图。

在两种情况下,我们都可以使用GraalVM的polyglot API,也可以将其他语言的结果组合到JavaScript中。

const express = require('express')const app = express()const BigInteger = Java.type('java.math.BigInteger')app.get('/', function (req, res) {  var text = 'Hello World from Graal.js!<br> '  // Using Java standard library classes  text += BigInteger.valueOf(10).pow(100)          .add(BigInteger.valueOf(43)).toString() + '<br>'  // Using R interoperability to create graphs  text += Polyglot.eval('R',    `svg();     require(lattice);     x <- 1:100     y <- sin(x/10)     z <- cos(x^1.3/(runif(1)*5+10))     print(cloud(x~y*z, main="cloud plot"))     grDevices:::svg.off()    `);  res.send(text)})app.listen(3000, function () {  console.log('Example app listening on port 3000!')})

在浏览器中打开http:// localhost:3000 /以查看结果。

这是我们使用GraalVM可以做的第三件事-运行以多种语言编写的程序,并一起使用这些语言中的模块。 我们认为这是一种语言和模块的商品化,您可以使用最适合您的问题的任何一种语言,以及想要的任何一种库,无论它来自哪种语言。

4.在JVM上运行本地语言

GraalVM支持的另一种语言是C。GraalVM可以以与运行JavaScript和Ruby等语言相同的方式运行C代码。

GraalVM实际支持的是运行LLVM工具链的输出(即LLVM位代码),而不是直接支持C。这意味着您可以将现有工具与C以及其他可以输出LLVM的语言一起使用,例如C ++,Fortran和可能的语言。 其他语言。 为了使演示更简单,我运行了由Stephen McCamant维护的特殊单文件版gzip。 为了简单起见,仅将gzip源代码和autoconf配置连接到一个文件中。 我还必须修补一些东西才能使其在macOS和clang上运行,但不能使其在GraalVM上运行。

$ clang -c -emit-llvm gzip.c

然后,我们使用lli命令(LLVM位码解释器)直接使用GraalVM运行此程序。 让我们尝试使用我的系统gzip压缩文件,然后使用在GraalVM上运行的gzip解压缩。

$ cat small.txtLorem ipsum dolor sit amet...$ gzip small.txt$ lli gzip.bc -d small.txt.gz$ cat small.txtLorem ipsum dolor sit amet...

另外,也可以使用GraalVM附带的clang将C / C ++代码编译为LLVM位代码。 为此,您应该启用预构建的LLVM工具链支持,并将LLVM_TOOLCHAIN环境变量指向包含一组构建工具(例如C编译器和链接器)的目录,该目录允许将本机项目编译为位代码。

$ gu install llvm-toolchain$ export LLVM_TOOLCHAIN=$(lli --print-toolchain-path)

然后,您可以将gzip.c源代码编译为具有嵌入式LLVM位代码的可执行文件,并按以下方式运行它:

$ $LLVM_TOOLCHAIN/clang gzip.c -o gzip$ gzip small.txt$ lli gzip -d small.txt.gz$ cat small.txtLorem ipsum dolor sit amet...

GraalVM中的Ruby和Python实现使用此技术来为这些语言运行C扩展。 这意味着您可以在VM内运行C扩展,并且即使我们支持这些旧的本机扩展接口,也可以使我们保持高性能。

这是使用GraalVM要做的第四件事-运行以C和C ++等本机语言编写的程序,并运行对诸如Python和Ruby之类的语言的C扩展,而现有的JVM实现(如JRuby)则无法做到。

5.适用于所有语言的工具

如果您使用Java编程,则可能会习惯使用非常高质量的工具,例如IDE,调试器和分析器。 并非所有语言都具有这类工具,但是如果您在GraalVM中使用一种语言,则可以使用它们。

所有GraalVM语言(目前不包括Java)都是使用通用的Truffle框架实现的。 这使我们可以一次实现调试器之类的功能,并使其可用于所有语言。

要尝试此操作,我们将编写一个基本的FizzBuzz程序,因为该程序将内容打印到屏幕上,并且具有仅在某些迭代中使用的清晰分支,因此我们可以更轻松地设置一些断点。 我们将从JavaScript实现开始。

function fizzbuzz(n) {  if ((n % 3 == 0) && (n % 5 == 0)) {    return 'FizzBuzz';  } else if (n % 3 == 0) {    return 'Fizz';  } else if (n % 5 == 0) {    return 'Buzz';  } else {    return n;  }}for (var n = 1; n <= 20; n++) {  print(fizzbuzz(n));}

我们可以使用js可执行文件,使用GraalVM正常运行此JavaScript程序。

$ js fizzbuzz.js12Fizz4BuzzFizz...

我们还可以使用–inspect标志运行该程序。 这将为我们提供一个可在Chrome中打开的链接,并将在调试器中暂停该程序。

$ js --inspect fizzbuzz.jsDebugger listening on port 9229.To start debugging, open the following URL in Chrome:    chrome-devtools://devtools/bundled/inspector.html?ws=127.0.0.1:9229/6c478d4e-1350b196b409...

然后,我们可以在FizzBuzz行上设置一个断点,然后继续执行。 中断时,我们将看到n的值,并且可以再次继续,或浏览调试接口的其余部分。

Chrome调试器通常与JavaScript一起使用,但是GraalVM中的JavaScript没有什么特别之处。 该标志也可用,并且可以在我们的Python,Ruby和R的实现中使用。我不会向您显示每个程序的源代码,但是您将以完全相同的方式运行它们,并为每个程序获得相同的Chrome调试器接口。

$ graalpython --inspect fizzbuzz.py
$ ruby --inspect fizzbuzz.rb
$ Rscript --inspect fizzbuzz.r

您可能已经从Java使用过的另一个工具是VisualVM。 它提供了一个用户界面,您可以将其连接到计算机上或网络上某个位置上正在运行的JVM,以检查各个方面,例如它如何使用内存和线程。

GraalVM将VisualVM包含在标准jvisualvm命令中。

$ jvisualvm &> /dev/null &

如果在从前运行Java TopTen应用程序时运行它,我们可以观察一段时间内的内存使用情况,也可以执行堆转储并检查在堆中使用内存的对象类型。

$ java TopTen large.txt

我已经编写了这个Ruby程序,以便随着时间的推移产生一些垃圾。

require 'erb'x = 42template = ERB.new <<-EOF  The value of x is: <%= x %>EOFloop do  puts template.result(binding)end

如果您在VisualVM上运行JRuby之类的标准JVM语言,您会失望的是,您将看到底层的Java对象,而不是有关该语言对象的任何信息。

如果我们改用GraalVM版本的Ruby,VisualVM将识别Ruby对象本身。 我们需要使用–jvm命令来使用VisualVM,因为它不支持Ruby的本机版本。

$ ruby --jvm render.rb

如果需要,我们可以看到相同的基础Java对象的堆视图转储,或者在"摘要"下可以选择Ruby Heap并查看适当的Ruby对象。

Truffle框架是语言和工具的一种联系。 如果您使用Truffle编程语言,并根据Truffle的工具API编程此类调试器之类的工具,则每种工具均可使用每种语言,并且只需要编写一次该工具即可。

因此,您可以使用GraalVM的第五种方式是作为一个平台来获取语言的高质量工具,这些语言并不总是支持它们来构建定制工具,例如Chrome Debugger或VisualVM。

6.扩展基于JVM的应用程序

这些语言和工具不仅可以用作独立的语言实现,而且可以在多种语言的用例中一起使用,也可以嵌入到Java应用程序中。 新的org.graalvm.polyglot API使您可以加载和运行其他语言的代码,并使用这些语言中的值。

import org.graalvm.polyglot.Context;import org.graalvm.polyglot.Value;public class ExtendJava {    public static void main(String[] args) {        String language = "js";        try (Context context = Context.newBuilder().allowNativeAccess(true).build()) {            for (String arg : args) {                if (arg.startsWith("-")) {                    language = arg.substring(1);                } else {                    Value v = context.eval(language, arg);                    System.out.println(v);                }            }        }    }}

如果使用GraalVM中的javac和java命令,则导入org.graalvm …将已经在您的类路径中,因此您可以编译并运行它而无需任何其他标志。

$ javac ExtendJava.java$ java ExtendJava '14 + 2'16$ java ExtendJava -js 'Math.sqrt(14)'3.7416573867739413$ java ExtendJava -python '[2**n for n in range(0, 8)]'[1, 2, 4, 8, 16, 32, 64, 128]$ java ExtendJava -ruby '[4, 2, 3].sort'[2, 3, 4]

这些版本的语言与使用node和ruby等命令作为GraalVM可执行文件所获得的高性能多语言版本相同。

这是您可以使用GraalVM的第六种方法-作为将Java语言中嵌入许多不同语言的单个界面。 使用polyglot API,您可以获取来宾语言对象并将其用作Java接口和其他复杂的互操作性。

7.扩展本机应用程序

GraalVM已经包括一个像这样构建的本机库,它是一个库,可让您运行从本机应用程序以任何GraalVM语言编写的代码。 像V8这样的JavaScript运行时,以及像CPython这样的Python解释程序,通常都是可嵌入的,这意味着它们可以作为一个库链接到另一个应用程序。 通过链接到这一多语言嵌入库,GraalVM允许您在嵌入式上下文中使用任何语言。

获取GraalVM时已经构建了该库,但是默认情况下它仅包含内置语言JavaScript。 您可以使用以下命令重建多语言库,以包含其他语言,但您需要从OTN下载基于Mac OS(19.3.0)的JDK8的Oracle GraalVM Enterprise Edition本机映像早期采用者。 重建过程确实需要花费几分钟,因此,如果您继续遵循,可能只想尝试使用JavaScript-如果只需要JavaScript,就不需要重建。

$ gu install --force --file native-image-installable-svm-svmee-java8-darwin-amd64-19.3.0.jar$ gu rebuild-images libpolyglot

我们可以编写一个简单的C程序,以任何通过命令行传递的GraalVM语言运行命令。 我们将与上面的ExtendJava示例等效,但是将C作为宿主语言。

#include <stdlib.h>#include <stdio.h>#include <polyglot_api.h>int main(int argc, char **argv) {  poly_isolate isolate = NULL;  poly_thread thread = NULL;    if (poly_create_isolate(NULL, &isolate, &thread) != poly_ok) {    fprintf(stderr, "poly_create_isolate error\n");    return 1;  }    poly_context context = NULL;    if (poly_create_context(thread, NULL, 0, &context) != poly_ok) {    fprintf(stderr, "poly_create_context error\n");    goto exit_isolate;  }    char* language = "js";    for (int n = 1; n < argc; n++) {    if (argv[n][0] == '-') {      language = &argv[n][1];    } else {      poly_value result = NULL;      if (poly_open_handle_scope(thread) != poly_ok) {        fprintf(stderr, "poly_open_handle_scope error\n");        goto exit_context;      }            if (poly_context_eval(thread, context, language, "eval", argv[n], &result) != poly_ok) {        fprintf(stderr, "poly_context_eval error\n");                const poly_extended_error_info *error;                if (poly_get_last_error_info(thread, &error) != poly_ok) {          fprintf(stderr, "poly_get_last_error_info error\n");          goto exit_scope;        }        fprintf(stderr, "%s\n", error->error_message);        goto exit_scope;      }            char buffer[1024];      size_t length;            if (poly_value_to_string_utf8(thread, result, buffer, sizeof(buffer), &length) != poly_ok) {        fprintf(stderr, "poly_value_to_string_utf8 error\n");        goto exit_scope;      }      if (poly_close_handle_scope(thread) != poly_ok) {        fprintf(stderr, "poly_close_handle_scope error\n");        goto exit_context;      }      buffer[length] = '\0';      printf("%s\n", buffer);    }  }  if (poly_context_close(thread, context, true) != poly_ok) {    fprintf(stderr, "poly_context_close error\n");    goto exit_isolate;  }  if (poly_tear_down_isolate(thread) != poly_ok) {    fprintf(stderr, "poly_tear_down_isolate error\n");    return 1;  }    return 0;exit_scope:  poly_close_handle_scope(thread);exit_context:  poly_context_close(thread, context, true);exit_isolate:  poly_tear_down_isolate(thread);  return 1;}

然后,我们可以使用我们的系统C编译器来编译和运行该程序,并链接到GraalVM中的本机多语言库。 同样,它不需要JVM。

$ clang -L$GRAALVM_HOME/jre/lib/polyglot -I${GRAALVM_HOME}/jre/lib/polyglot -lpolyglot -o extendc -O1 extendc.c -rpath $GRAALVM_HOME$ otool -L extendcextendc:  @rpath/jre/lib/polyglot/libpolyglot.dylib (compatibility version       0.0.0, current version 0.0.0)  /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1252.250.1)
$ ./extendc '14 + 2'16$ ./extendc -js 'Math.sqrt(14)'3.7416573867739413$ ./extendc -python '[2**n for n in range(0, 8)]'[1, 2, 4, 8, 16, 32, 64, 128]$ ./extendc -ruby '(0...8).map { |n| 2 ** n }'[1, 2, 4, 8, 16, 32, 64, 128]

使用GraalVM可以完成的第七件事–在本机应用程序中使用单个库来嵌入任何GraalVM语言。

8. Java代码作为本机库

Java具有许多高质量的库的强大生态系统,而其他生态系统(包括本机应用程序和其他托管语言)通常无法提供这些库。 如果您想使用本机应用程序中的Java库,则可以嵌入JVM,但这会变得非常庞大和复杂。

GraalVM可让您使用现成的Java库或自己编写的Java库,然后将其编译成独立的本机库以供其他本机语言使用。 与以前的本机编译一样,它们不需要运行JVM。

我编写了一个应用程序,该应用程序使用了出色的Apache SIS地理空间库来计算地球上两点之间的大圆距离。 我使用SIS 0.8,我从单独下载并从中提取了jar。

import org.apache.sis.distance.DistanceUtils;public class Distance {    public static void main(String[] args) {        final double aLat   = Double.parseDouble(args[0]);        final double aLong  = Double.parseDouble(args[1]);        final double bLat   = Double.parseDouble(args[2]);        final double bLong  = Double.parseDouble(args[3]);        System.out.printf("%f km%n", DistanceUtils.getHaversineDistance(aLat, aLong, bLat, bLong));    }}

我们可以像平常一样编译它,然后用它来计算伦敦(纬度51.507222,经度-0.1275)和纽约(40.7127,-74.0059)之间的距离。

$ javac -cp sis.jar -parameters Distance.java$ java -cp sis.jar:. Distance 51.507222 -0.1275 40.7127 -74.00595570.25 km

就像我们使用topten程序一样,我们可以将其编译为本地可执行文件。

$ native-image --no-server --no-fallback -cp sis.jar:. Distance...$ ./distance 51.507222 -0.1275 40.7127 -74.00595570.25 km

我们还可以将其构建为本地共享库,而不是可执行文件。 为此,我们将一个或多个方法声明为@CEntryPoint。

...import org.graalvm.nativeimage.IsolateThread;import org.graalvm.nativeimage.c.function.CEntryPoint;public class Distance {    ...    @CEntryPoint(name = "distance")    public static double distance(IsolateThread thread,          double a_lat, double a_long,          double b_lat, double b_long) {        return DistanceUtils.getHaversineDistance(a_lat, a_long, b_lat, b_long);    }        ...}

我们不需要更改javac命令行,因为GraalVM会自动将这些新API放到类路径中。 然后,我们可以编译为共享库和自动生成的头文件。

$ native-image --no-server -cp sis.jar:. --shared -H:Name=libdistance$ otool -L libdistance.dylib   # .so on Linuxlibdistance.dylib:  .../graalvm-ten-things/libdistance.dylib (compatibility version 0.0.0, current version 0.0.0)/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1252.250.1)CoreFoundation (compatibility version 150.0.0, current version 1575.17.0)/usr/lib/libz.1.dylib (compatibility version 1.0.0, current version 1.2.11)$ du -h libdistance.dylib1.8M  libdistance.dylib

然后,我们可以编写一些C程序来使用该库。 与本机库的接口确实有一个小小的仪式-因为VM需要管理堆,线程,垃圾收集器和其他服务,因此我们需要创建系统实例,并向其介绍我们的主线程。

#include <stdlib.h>#include <stdio.h>#include <libdistance.h>int main(int argc, char **argv) {  graal_isolate_t *isolate = NULL;  graal_isolatethread_t *thread = NULL;    if (graal_create_isolate(NULL, &isolate, &thread) != 0) {    fprintf(stderr, "graal_create_isolate error\n");    return 1;  }    double a_lat   = strtod(argv[1], NULL);  double a_long  = strtod(argv[2], NULL);  double b_lat   = strtod(argv[3], NULL);  double b_long  = strtod(argv[4], NULL);    printf("%.2f km\n", distance(thread, a_lat, a_long, b_lat, b_long));  if (graal_detach_thread(thread) != 0) {    fprintf(stderr, "graal_detach_thread error\n");    return 1;  }    return 0;}

我们使用标准系统工具对此进行编译,并且可以运行我们的可执行文件(在Linux上设置LD_LIBRARY_PATH =。)。

$ clang -I. -L. -ldistance distance.c -o distance$ otool -L distancedistance:.../graalvm-blog-post/libdistance.dylib (compatibility version 0.0.0, current version 0.0.0)libSystem.B.dylib (compatibility version 1.0.0, current version 1252.250.1)$ ./distance 51.507222 -0.1275 40.7127 -74.00595570.25 km

这是我们使用GraalVM可以完成的第八件事-将Java代码编译为一个本机库,然后可以在本机应用程序中使用它而无需使用完整的JVM。

9.数据库中的多语种

用于嵌入语言的polyglot库的一种应用是在Oracle数据库中。 我们用它来创建Oracle数据库多语言引擎(MLE),其中包括对使用GraalVM语言和SQL模块的支持。

例如,假设我们已经有一个用JavaScript编写的前端,并且我们正在使用JavaScript模块验证器对电子邮件地址进行一些验证。 如果我们对用SQL或PLSQL编写的数据库中的同一应用程序具有某种逻辑,我们希望能够使用完全相同的验证器,以使结果相同。

您可以从下载MLE作为Docker映像。 然后将其加载到Docker中。

$ docker load --input mle-docker-0.2.7.tar.gz

我们要运行该图像,然后在完成加载后(可能需要几分钟),在其中执行一个Bash终端。

$ docker run mle-docker-0.2.7$ docker ps$ docker exec -ti <container_id> bash -li

如果我们可以在此Bash终端中运行交互式SQL工具sqlplus来连接到数据库,则它已启动并正在运行。

$ sqlplus scott/tiger@localhost:1521/ORCLCDB

现在,仍然在Docker中运行的Bash终端中,我们安装该验证程序模块,然后运行命令dbjs将其部署到数据库中。 然后,我们再次运行sqlplus。

$ npm install validator$ npm install @types/validator$ dbjs deploy -u scott -p tiger -c localhost:1521/ORCLCDB validator$ sqlplus scott/tiger@localhost:1521/ORCLCDB

现在,我们可以将验证器模块用作SQL表达式的一部分。

SQL> select validator.isEmail('hello.world@oracle.com') from dual;VALIDATOR.ISEMAIL('HELLO.WORLD@ORACLE.COM')-------------------------------------------                                          1SQL> select validator.isEmail('hello.world') from dual;VALIDATOR.ISEMAIL('HELLO.WORLD')--------------------------------                               0

这是我们使用GraalVM可以做的第九件事-在Oracle数据库中运行GraalVM语言,以便您可以在数据库逻辑中的前端或后端使用相同的逻辑,而不必始终将其从数据库中拉出到 应用服务器。

10.创建自己的语言

Oracle实验室和我们的学术合作者已经能够用一个相对较小的团队来实现JavaScript,R,Ruby,Python和C的新高性能实现,因为我们已经开发了Truffle框架来简化这一过程。

Truffle是一个Java库,可帮助您编写语言的抽象语法树(AST)解释程序。 AST解释器可能是实现语言的最简单方法,因为它可以直接在解析器的输出上运行,并且不涉及任何字节码或常规的编译器技术,但通常速度较慢。 因此,我们将其与称为部分评估的技术结合在一起,该技术允许Truffle仅仅基于AST解释器就可以使用GraalVM编译器为您的语言自动提供即时编译。

您可以使用Truffle来实现自己的新编程语言,创建现有编程语言的高性能实现或实现特定于域的语言。 我们在项目中谈论了很多关于Truffle和Graal的细节,但是我们常常忘记提及Truffle是实现语言的简单方法。 您会自动获得调试器之类的功能。 任何只完成了编程语言实施本科课程的人都应该具备所需的基本技能。 Oracle实验室只需几个月的实习生,就能比以前的任何工作更快地实现Ruby的基本版本。

我们这里没有空间来显示完整的语言,即使是很小的一种语言,但SimpleLanguage是一个可执行的教程,说明如何使用Truffle,以简化的JavaScript样式语言为基础来创建自己的语言。 例如,查看if语句的实现。

Oracle Labs以外的其他人使用Truffle编写的其他语言包括Smalltalk变体,Newspeak变体和Lisp变体。 Lisp示例包含一个您可以遵循的教程。

结论

GraalVM提供了非常多样化的新功能集–在该平台上,您可以构建更强大的语言和工具,并将其置于更多环境中。 无论程序在何处运行或已经在使用哪种语言,它都使您可以选择所需的语言和模块。

要尝试GraalVM,请访问。 那里有下载和文档的链接,还有更多类似我们在此博客文章中显示的示例。

尝试按照此处的说明进行操作,并尝试对其进行调整,以了解您还可以做什么。 让我们知道您如何在应用程序中使用GraalVM,并向我们发送任何反馈@ChrisGSeaton或@shelajev。

(本文翻译自Chris Seaton的文章《Top 10 Things To Do With GraalVM》,参考:)

标签: #apache来宾用户